]>
Commit | Line | Data |
---|---|---|
3d0480ed AK |
1 | /* |
2 | * Copyright (C) 2005 Red Hat, Inc. All rights reserved. | |
3 | * | |
4 | * This file is part of the device-mapper userspace tools. | |
5 | * | |
6 | * This copyrighted material is made available to anyone wishing to use, | |
7 | * modify, copy, or redistribute it subject to the terms and conditions | |
8 | * of the GNU Lesser General Public License v.2.1. | |
9 | * | |
10 | * You should have received a copy of the GNU Lesser General Public License | |
11 | * along with this program; if not, write to the Free Software Foundation, | |
12 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
13 | */ | |
14 | ||
15 | #include "lib.h" | |
16 | #include "libdm-targets.h" | |
17 | #include "libdm-common.h" | |
18 | #include "list.h" | |
19 | #include "kdev_t.h" | |
3d0480ed AK |
20 | |
21 | #include <stdarg.h> | |
22 | #include <sys/param.h> | |
23 | ||
24 | #include <linux/dm-ioctl.h> | |
25 | ||
165e4a11 AK |
26 | #define MAX_TARGET_PARAMSIZE 500000 |
27 | ||
87f98002 AK |
28 | /* FIXME Fix interface so this is used only by LVM */ |
29 | #define UUID_PREFIX "LVM-" | |
30 | ||
165e4a11 AK |
31 | /* Supported segment types */ |
32 | enum { | |
33 | SEG_ERROR, | |
34 | SEG_LINEAR, | |
35 | SEG_MIRRORED, | |
36 | SEG_SNAPSHOT, | |
37 | SEG_SNAPSHOT_ORIGIN, | |
38 | SEG_STRIPED, | |
39 | SEG_ZERO, | |
40 | }; | |
b4f1578f | 41 | |
165e4a11 AK |
42 | /* FIXME Add crypt and multipath support */ |
43 | ||
44 | struct { | |
45 | unsigned type; | |
46 | const char *target; | |
47 | } dm_segtypes[] = { | |
48 | { SEG_ERROR, "error" }, | |
49 | { SEG_LINEAR, "linear" }, | |
50 | { SEG_MIRRORED, "mirror" }, | |
51 | { SEG_SNAPSHOT, "snapshot" }, | |
52 | { SEG_SNAPSHOT_ORIGIN, "snapshot-origin" }, | |
53 | { SEG_STRIPED, "striped" }, | |
54 | { SEG_ZERO, "zero"}, | |
55 | }; | |
56 | ||
57 | /* Some segment types have a list of areas of other devices attached */ | |
58 | struct seg_area { | |
59 | struct list list; | |
60 | ||
b4f1578f | 61 | struct dm_tree_node *dev_node; |
165e4a11 AK |
62 | |
63 | uint64_t offset; | |
64 | }; | |
65 | ||
66 | /* Per-segment properties */ | |
67 | struct load_segment { | |
68 | struct list list; | |
69 | ||
70 | unsigned type; | |
71 | ||
72 | uint64_t size; | |
73 | ||
74 | unsigned area_count; /* Linear + Striped + Mirrored */ | |
75 | struct list areas; /* Linear + Striped + Mirrored */ | |
76 | ||
77 | uint32_t stripe_size; /* Striped */ | |
78 | ||
79 | int persistent; /* Snapshot */ | |
80 | uint32_t chunk_size; /* Snapshot */ | |
b4f1578f AK |
81 | struct dm_tree_node *cow; /* Snapshot */ |
82 | struct dm_tree_node *origin; /* Snapshot + Snapshot origin */ | |
165e4a11 | 83 | |
b4f1578f | 84 | struct dm_tree_node *log; /* Mirror */ |
165e4a11 AK |
85 | uint32_t region_size; /* Mirror */ |
86 | unsigned clustered; /* Mirror */ | |
87 | unsigned mirror_area_count; /* Mirror */ | |
88 | }; | |
89 | ||
90 | /* Per-device properties */ | |
91 | struct load_properties { | |
92 | int read_only; | |
93 | uint32_t major; | |
94 | uint32_t minor; | |
95 | ||
96 | unsigned segment_count; | |
97 | struct list segs; | |
98 | ||
99 | const char *new_name; | |
100 | }; | |
101 | ||
102 | /* Two of these used to join two nodes with uses and used_by. */ | |
b4f1578f | 103 | struct dm_tree_link { |
165e4a11 | 104 | struct list list; |
b4f1578f | 105 | struct dm_tree_node *node; |
165e4a11 AK |
106 | }; |
107 | ||
b4f1578f AK |
108 | struct dm_tree_node { |
109 | struct dm_tree *dtree; | |
3d0480ed AK |
110 | |
111 | const char *name; | |
112 | const char *uuid; | |
113 | struct dm_info info; | |
114 | ||
115 | struct list uses; /* Nodes this node uses */ | |
116 | struct list used_by; /* Nodes that use this node */ | |
165e4a11 | 117 | |
56c28292 AK |
118 | int activation_priority; /* 0 gets activated first */ |
119 | ||
165e4a11 AK |
120 | void *context; /* External supplied context */ |
121 | ||
122 | struct load_properties props; /* For creation/table (re)load */ | |
3d0480ed AK |
123 | }; |
124 | ||
b4f1578f | 125 | struct dm_tree { |
a3f6b2ce AK |
126 | struct dm_pool *mem; |
127 | struct dm_hash_table *devs; | |
165e4a11 | 128 | struct dm_hash_table *uuids; |
b4f1578f | 129 | struct dm_tree_node root; |
3d0480ed AK |
130 | }; |
131 | ||
165e4a11 AK |
132 | /* FIXME Consider exporting this */ |
133 | static int _dm_snprintf(char *buf, size_t bufsize, const char *format, ...) | |
134 | { | |
135 | int n; | |
136 | va_list ap; | |
137 | ||
138 | va_start(ap, format); | |
139 | n = vsnprintf(buf, bufsize, format, ap); | |
140 | va_end(ap); | |
141 | ||
142 | if (n < 0 || (n > bufsize - 1)) | |
143 | return -1; | |
144 | ||
145 | return n; | |
146 | } | |
3d0480ed | 147 | |
b4f1578f | 148 | struct dm_tree *dm_tree_create(void) |
3d0480ed | 149 | { |
b4f1578f | 150 | struct dm_tree *dtree; |
3d0480ed | 151 | |
b4f1578f AK |
152 | if (!(dtree = dm_malloc(sizeof(*dtree)))) { |
153 | log_error("dm_tree_create malloc failed"); | |
3d0480ed AK |
154 | return NULL; |
155 | } | |
156 | ||
b4f1578f AK |
157 | memset(dtree, 0, sizeof(*dtree)); |
158 | dtree->root.dtree = dtree; | |
159 | list_init(&dtree->root.uses); | |
160 | list_init(&dtree->root.used_by); | |
3d0480ed | 161 | |
b4f1578f AK |
162 | if (!(dtree->mem = dm_pool_create("dtree", 1024))) { |
163 | log_error("dtree pool creation failed"); | |
164 | dm_free(dtree); | |
3d0480ed AK |
165 | return NULL; |
166 | } | |
167 | ||
b4f1578f AK |
168 | if (!(dtree->devs = dm_hash_create(8))) { |
169 | log_error("dtree hash creation failed"); | |
170 | dm_pool_destroy(dtree->mem); | |
171 | dm_free(dtree); | |
3d0480ed AK |
172 | return NULL; |
173 | } | |
174 | ||
b4f1578f AK |
175 | if (!(dtree->uuids = dm_hash_create(32))) { |
176 | log_error("dtree uuid hash creation failed"); | |
177 | dm_hash_destroy(dtree->devs); | |
178 | dm_pool_destroy(dtree->mem); | |
179 | dm_free(dtree); | |
165e4a11 AK |
180 | return NULL; |
181 | } | |
182 | ||
b4f1578f | 183 | return dtree; |
3d0480ed AK |
184 | } |
185 | ||
b4f1578f | 186 | void dm_tree_free(struct dm_tree *dtree) |
3d0480ed | 187 | { |
b4f1578f | 188 | if (!dtree) |
3d0480ed AK |
189 | return; |
190 | ||
b4f1578f AK |
191 | dm_hash_destroy(dtree->uuids); |
192 | dm_hash_destroy(dtree->devs); | |
193 | dm_pool_destroy(dtree->mem); | |
194 | dm_free(dtree); | |
3d0480ed AK |
195 | } |
196 | ||
b4f1578f AK |
197 | static int _nodes_are_linked(struct dm_tree_node *parent, |
198 | struct dm_tree_node *child) | |
3d0480ed | 199 | { |
b4f1578f | 200 | struct dm_tree_link *dlink; |
3d0480ed | 201 | |
165e4a11 | 202 | list_iterate_items(dlink, &parent->uses) |
3d0480ed AK |
203 | if (dlink->node == child) |
204 | return 1; | |
3d0480ed AK |
205 | |
206 | return 0; | |
207 | } | |
208 | ||
b4f1578f | 209 | static int _link(struct list *list, struct dm_tree_node *node) |
3d0480ed | 210 | { |
b4f1578f | 211 | struct dm_tree_link *dlink; |
3d0480ed | 212 | |
b4f1578f AK |
213 | if (!(dlink = dm_pool_alloc(node->dtree->mem, sizeof(*dlink)))) { |
214 | log_error("dtree link allocation failed"); | |
3d0480ed AK |
215 | return 0; |
216 | } | |
217 | ||
218 | dlink->node = node; | |
219 | list_add(list, &dlink->list); | |
220 | ||
221 | return 1; | |
222 | } | |
223 | ||
b4f1578f AK |
224 | static int _link_nodes(struct dm_tree_node *parent, |
225 | struct dm_tree_node *child) | |
3d0480ed AK |
226 | { |
227 | if (_nodes_are_linked(parent, child)) | |
228 | return 1; | |
229 | ||
230 | if (!_link(&parent->uses, child)) | |
231 | return 0; | |
232 | ||
233 | if (!_link(&child->used_by, parent)) | |
234 | return 0; | |
235 | ||
236 | return 1; | |
237 | } | |
238 | ||
b4f1578f | 239 | static void _unlink(struct list *list, struct dm_tree_node *node) |
3d0480ed | 240 | { |
b4f1578f | 241 | struct dm_tree_link *dlink; |
3d0480ed | 242 | |
165e4a11 | 243 | list_iterate_items(dlink, list) |
3d0480ed AK |
244 | if (dlink->node == node) { |
245 | list_del(&dlink->list); | |
246 | break; | |
247 | } | |
3d0480ed AK |
248 | } |
249 | ||
b4f1578f AK |
250 | static void _unlink_nodes(struct dm_tree_node *parent, |
251 | struct dm_tree_node *child) | |
3d0480ed AK |
252 | { |
253 | if (!_nodes_are_linked(parent, child)) | |
254 | return; | |
255 | ||
256 | _unlink(&parent->uses, child); | |
257 | _unlink(&child->used_by, parent); | |
258 | } | |
259 | ||
b4f1578f | 260 | static int _add_to_toplevel(struct dm_tree_node *node) |
165e4a11 | 261 | { |
b4f1578f | 262 | return _link_nodes(&node->dtree->root, node); |
165e4a11 AK |
263 | } |
264 | ||
b4f1578f | 265 | static void _remove_from_toplevel(struct dm_tree_node *node) |
3d0480ed | 266 | { |
b4f1578f | 267 | return _unlink_nodes(&node->dtree->root, node); |
3d0480ed AK |
268 | } |
269 | ||
b4f1578f | 270 | static int _add_to_bottomlevel(struct dm_tree_node *node) |
3d0480ed | 271 | { |
b4f1578f | 272 | return _link_nodes(node, &node->dtree->root); |
3d0480ed AK |
273 | } |
274 | ||
b4f1578f | 275 | static void _remove_from_bottomlevel(struct dm_tree_node *node) |
165e4a11 | 276 | { |
b4f1578f | 277 | return _unlink_nodes(node, &node->dtree->root); |
165e4a11 AK |
278 | } |
279 | ||
b4f1578f | 280 | static int _link_tree_nodes(struct dm_tree_node *parent, struct dm_tree_node *child) |
165e4a11 AK |
281 | { |
282 | /* Don't link to root node if child already has a parent */ | |
b4f1578f AK |
283 | if ((parent == &parent->dtree->root)) { |
284 | if (dm_tree_node_num_children(child, 1)) | |
165e4a11 AK |
285 | return 1; |
286 | } else | |
287 | _remove_from_toplevel(child); | |
288 | ||
b4f1578f AK |
289 | if ((child == &child->dtree->root)) { |
290 | if (dm_tree_node_num_children(parent, 0)) | |
165e4a11 AK |
291 | return 1; |
292 | } else | |
293 | _remove_from_bottomlevel(parent); | |
294 | ||
295 | return _link_nodes(parent, child); | |
296 | } | |
297 | ||
b4f1578f | 298 | static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree, |
3d0480ed AK |
299 | const char *name, |
300 | const char *uuid, | |
165e4a11 AK |
301 | struct dm_info *info, |
302 | void *context) | |
3d0480ed | 303 | { |
b4f1578f | 304 | struct dm_tree_node *node; |
3d0480ed AK |
305 | uint64_t dev; |
306 | ||
b4f1578f AK |
307 | if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) { |
308 | log_error("_create_dm_tree_node alloc failed"); | |
3d0480ed AK |
309 | return NULL; |
310 | } | |
311 | ||
b4f1578f | 312 | node->dtree = dtree; |
3d0480ed AK |
313 | |
314 | node->name = name; | |
315 | node->uuid = uuid; | |
316 | node->info = *info; | |
165e4a11 | 317 | node->context = context; |
56c28292 | 318 | node->activation_priority = 0; |
3d0480ed AK |
319 | |
320 | list_init(&node->uses); | |
321 | list_init(&node->used_by); | |
165e4a11 | 322 | list_init(&node->props.segs); |
3d0480ed AK |
323 | |
324 | dev = MKDEV(info->major, info->minor); | |
325 | ||
b4f1578f | 326 | if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev, |
3d0480ed | 327 | sizeof(dev), node)) { |
b4f1578f AK |
328 | log_error("dtree node hash insertion failed"); |
329 | dm_pool_free(dtree->mem, node); | |
3d0480ed AK |
330 | return NULL; |
331 | } | |
332 | ||
165e4a11 | 333 | if (uuid && *uuid && |
b4f1578f AK |
334 | !dm_hash_insert(dtree->uuids, uuid, node)) { |
335 | log_error("dtree uuid hash insertion failed"); | |
336 | dm_hash_remove_binary(dtree->devs, (const char *) &dev, | |
165e4a11 | 337 | sizeof(dev)); |
b4f1578f | 338 | dm_pool_free(dtree->mem, node); |
165e4a11 AK |
339 | return NULL; |
340 | } | |
341 | ||
3d0480ed AK |
342 | return node; |
343 | } | |
344 | ||
b4f1578f | 345 | static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree, |
3d0480ed AK |
346 | uint32_t major, uint32_t minor) |
347 | { | |
348 | uint64_t dev = MKDEV(major, minor); | |
349 | ||
b4f1578f | 350 | return dm_hash_lookup_binary(dtree->devs, (const char *) &dev, |
3d0480ed AK |
351 | sizeof(dev)); |
352 | } | |
353 | ||
b4f1578f | 354 | static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree, |
165e4a11 AK |
355 | const char *uuid) |
356 | { | |
87f98002 AK |
357 | struct dm_tree_node *node; |
358 | ||
359 | if ((node = dm_hash_lookup(dtree->uuids, uuid))) | |
360 | return node; | |
361 | ||
362 | if (strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) | |
363 | return NULL; | |
364 | ||
365 | return dm_hash_lookup(dtree->uuids, uuid + sizeof(UUID_PREFIX) - 1); | |
165e4a11 AK |
366 | } |
367 | ||
a3f6b2ce | 368 | static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor, |
3d0480ed AK |
369 | const char **name, const char **uuid, |
370 | struct dm_info *info, struct dm_deps **deps) | |
371 | { | |
372 | memset(info, 0, sizeof(*info)); | |
373 | ||
374 | if (!dm_is_dm_major(major)) { | |
375 | *name = ""; | |
376 | *uuid = ""; | |
377 | *deps = NULL; | |
378 | info->major = major; | |
379 | info->minor = minor; | |
380 | info->exists = 0; | |
165e4a11 AK |
381 | info->live_table = 0; |
382 | info->inactive_table = 0; | |
383 | info->read_only = 0; | |
3d0480ed AK |
384 | return 1; |
385 | } | |
386 | ||
387 | if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) { | |
388 | log_error("deps dm_task creation failed"); | |
389 | return 0; | |
390 | } | |
391 | ||
b4f1578f AK |
392 | if (!dm_task_set_major(*dmt, major)) { |
393 | log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")", | |
394 | major, minor); | |
3d0480ed | 395 | goto failed; |
b4f1578f | 396 | } |
3d0480ed | 397 | |
b4f1578f AK |
398 | if (!dm_task_set_minor(*dmt, minor)) { |
399 | log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")", | |
400 | major, minor); | |
3d0480ed | 401 | goto failed; |
b4f1578f | 402 | } |
3d0480ed | 403 | |
b4f1578f AK |
404 | if (!dm_task_run(*dmt)) { |
405 | log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")", | |
406 | major, minor); | |
3d0480ed | 407 | goto failed; |
b4f1578f | 408 | } |
3d0480ed | 409 | |
b4f1578f AK |
410 | if (!dm_task_get_info(*dmt, info)) { |
411 | log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")", | |
412 | major, minor); | |
3d0480ed | 413 | goto failed; |
b4f1578f | 414 | } |
3d0480ed AK |
415 | |
416 | if (!info->exists) { | |
417 | *name = ""; | |
418 | *uuid = ""; | |
419 | *deps = NULL; | |
420 | } else { | |
421 | if (info->major != major) { | |
b4f1578f | 422 | log_error("Inconsistent dtree major number: %u != %u", |
3d0480ed AK |
423 | major, info->major); |
424 | goto failed; | |
425 | } | |
426 | if (info->minor != minor) { | |
b4f1578f | 427 | log_error("Inconsistent dtree minor number: %u != %u", |
3d0480ed AK |
428 | minor, info->minor); |
429 | goto failed; | |
430 | } | |
a3f6b2ce | 431 | if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) { |
3d0480ed AK |
432 | log_error("name pool_strdup failed"); |
433 | goto failed; | |
434 | } | |
a3f6b2ce | 435 | if (!(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) { |
3d0480ed AK |
436 | log_error("uuid pool_strdup failed"); |
437 | goto failed; | |
438 | } | |
439 | *deps = dm_task_get_deps(*dmt); | |
440 | } | |
441 | ||
442 | return 1; | |
443 | ||
444 | failed: | |
445 | dm_task_destroy(*dmt); | |
446 | return 0; | |
447 | } | |
448 | ||
b4f1578f AK |
449 | static struct dm_tree_node *_add_dev(struct dm_tree *dtree, |
450 | struct dm_tree_node *parent, | |
165e4a11 | 451 | uint32_t major, uint32_t minor) |
3d0480ed AK |
452 | { |
453 | struct dm_task *dmt = NULL; | |
454 | struct dm_info info; | |
455 | struct dm_deps *deps = NULL; | |
456 | const char *name = NULL; | |
457 | const char *uuid = NULL; | |
b4f1578f | 458 | struct dm_tree_node *node = NULL; |
3d0480ed | 459 | uint32_t i; |
3d0480ed AK |
460 | int new = 0; |
461 | ||
462 | /* Already in tree? */ | |
b4f1578f AK |
463 | if (!(node = _find_dm_tree_node(dtree, major, minor))) { |
464 | if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, &info, &deps)) | |
465 | return_NULL; | |
3d0480ed | 466 | |
b4f1578f | 467 | if (!(node = _create_dm_tree_node(dtree, name, uuid, |
165e4a11 | 468 | &info, NULL))) |
b4f1578f | 469 | goto_out; |
3d0480ed AK |
470 | new = 1; |
471 | } | |
472 | ||
165e4a11 AK |
473 | if (!_link_tree_nodes(parent, node)) { |
474 | node = NULL; | |
b4f1578f | 475 | goto_out; |
165e4a11 | 476 | } |
3d0480ed AK |
477 | |
478 | /* If node was already in tree, no need to recurse. */ | |
479 | if (!new) | |
165e4a11 | 480 | goto out; |
3d0480ed AK |
481 | |
482 | /* Can't recurse if not a mapped device or there are no dependencies */ | |
483 | if (!node->info.exists || !deps->count) { | |
b4f1578f AK |
484 | if (!_add_to_bottomlevel(node)) { |
485 | stack; | |
165e4a11 | 486 | node = NULL; |
b4f1578f | 487 | } |
165e4a11 | 488 | goto out; |
3d0480ed AK |
489 | } |
490 | ||
491 | /* Add dependencies to tree */ | |
492 | for (i = 0; i < deps->count; i++) | |
b4f1578f | 493 | if (!_add_dev(dtree, node, MAJOR(deps->device[i]), |
165e4a11 AK |
494 | MINOR(deps->device[i]))) { |
495 | node = NULL; | |
b4f1578f | 496 | goto_out; |
165e4a11 | 497 | } |
3d0480ed | 498 | |
3d0480ed AK |
499 | out: |
500 | if (dmt) | |
501 | dm_task_destroy(dmt); | |
502 | ||
165e4a11 AK |
503 | return node; |
504 | } | |
505 | ||
b4f1578f | 506 | static int _node_clear_table(struct dm_tree_node *dnode) |
165e4a11 AK |
507 | { |
508 | struct dm_task *dmt; | |
509 | struct dm_info *info; | |
510 | const char *name; | |
511 | int r; | |
512 | ||
513 | if (!(info = &dnode->info)) { | |
b4f1578f | 514 | log_error("_node_clear_table failed: missing info"); |
165e4a11 AK |
515 | return 0; |
516 | } | |
517 | ||
b4f1578f AK |
518 | if (!(name = dm_tree_node_get_name(dnode))) { |
519 | log_error("_node_clear_table failed: missing name"); | |
165e4a11 AK |
520 | return 0; |
521 | } | |
522 | ||
523 | /* Is there a table? */ | |
524 | if (!info->exists || !info->inactive_table) | |
525 | return 1; | |
526 | ||
527 | log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")", | |
528 | name, info->major, info->minor); | |
529 | ||
530 | if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) { | |
531 | dm_task_destroy(dmt); | |
532 | log_error("Table clear dm_task creation failed for %s", name); | |
533 | return 0; | |
534 | } | |
535 | ||
536 | if (!dm_task_set_major(dmt, info->major) || | |
537 | !dm_task_set_minor(dmt, info->minor)) { | |
538 | log_error("Failed to set device number for %s table clear", name); | |
539 | dm_task_destroy(dmt); | |
540 | return 0; | |
541 | } | |
542 | ||
543 | r = dm_task_run(dmt); | |
544 | ||
545 | if (!dm_task_get_info(dmt, info)) { | |
b4f1578f | 546 | log_error("_node_clear_table failed: info missing after running task for %s", name); |
165e4a11 AK |
547 | r = 0; |
548 | } | |
549 | ||
550 | dm_task_destroy(dmt); | |
551 | ||
3d0480ed AK |
552 | return r; |
553 | } | |
554 | ||
b4f1578f | 555 | struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree, |
165e4a11 AK |
556 | const char *name, |
557 | const char *uuid, | |
558 | uint32_t major, uint32_t minor, | |
559 | int read_only, | |
560 | int clear_inactive, | |
561 | void *context) | |
562 | { | |
b4f1578f | 563 | struct dm_tree_node *dnode; |
165e4a11 AK |
564 | struct dm_info info; |
565 | const char *name2; | |
566 | const char *uuid2; | |
567 | ||
568 | /* Do we need to add node to tree? */ | |
b4f1578f AK |
569 | if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) { |
570 | if (!(name2 = dm_pool_strdup(dtree->mem, name))) { | |
165e4a11 AK |
571 | log_error("name pool_strdup failed"); |
572 | return NULL; | |
573 | } | |
b4f1578f | 574 | if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) { |
165e4a11 AK |
575 | log_error("uuid pool_strdup failed"); |
576 | return NULL; | |
577 | } | |
578 | ||
579 | info.major = 0; | |
580 | info.minor = 0; | |
581 | info.exists = 0; | |
582 | info.live_table = 0; | |
583 | info.inactive_table = 0; | |
584 | info.read_only = 0; | |
585 | ||
b4f1578f AK |
586 | if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, |
587 | &info, context))) | |
588 | return_NULL; | |
165e4a11 AK |
589 | |
590 | /* Attach to root node until a table is supplied */ | |
b4f1578f AK |
591 | if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode)) |
592 | return_NULL; | |
165e4a11 AK |
593 | |
594 | dnode->props.major = major; | |
595 | dnode->props.minor = minor; | |
596 | dnode->props.new_name = NULL; | |
597 | } else if (strcmp(name, dnode->name)) { | |
598 | /* Do we need to rename node? */ | |
b4f1578f | 599 | if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) { |
165e4a11 AK |
600 | log_error("name pool_strdup failed"); |
601 | return 0; | |
602 | } | |
603 | } | |
604 | ||
605 | dnode->props.read_only = read_only ? 1 : 0; | |
606 | ||
b4f1578f AK |
607 | if (clear_inactive && !_node_clear_table(dnode)) |
608 | return_NULL; | |
165e4a11 AK |
609 | |
610 | dnode->context = context; | |
611 | ||
612 | return dnode; | |
613 | } | |
614 | ||
b4f1578f | 615 | int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor) |
3d0480ed | 616 | { |
b4f1578f | 617 | return _add_dev(dtree, &dtree->root, major, minor) ? 1 : 0; |
3d0480ed AK |
618 | } |
619 | ||
b4f1578f | 620 | const char *dm_tree_node_get_name(struct dm_tree_node *node) |
3d0480ed AK |
621 | { |
622 | return node->info.exists ? node->name : ""; | |
623 | } | |
624 | ||
b4f1578f | 625 | const char *dm_tree_node_get_uuid(struct dm_tree_node *node) |
3d0480ed AK |
626 | { |
627 | return node->info.exists ? node->uuid : ""; | |
628 | } | |
629 | ||
b4f1578f | 630 | const struct dm_info *dm_tree_node_get_info(struct dm_tree_node *node) |
3d0480ed AK |
631 | { |
632 | return &node->info; | |
633 | } | |
634 | ||
b4f1578f | 635 | void *dm_tree_node_get_context(struct dm_tree_node *node) |
165e4a11 AK |
636 | { |
637 | return node->context; | |
638 | } | |
639 | ||
b4f1578f | 640 | int dm_tree_node_num_children(struct dm_tree_node *node, uint32_t inverted) |
3d0480ed AK |
641 | { |
642 | if (inverted) { | |
b4f1578f | 643 | if (_nodes_are_linked(&node->dtree->root, node)) |
3d0480ed AK |
644 | return 0; |
645 | return list_size(&node->used_by); | |
646 | } | |
647 | ||
b4f1578f | 648 | if (_nodes_are_linked(node, &node->dtree->root)) |
3d0480ed AK |
649 | return 0; |
650 | ||
651 | return list_size(&node->uses); | |
652 | } | |
653 | ||
2b69db1f AK |
654 | /* |
655 | * Returns 1 if no prefix supplied | |
656 | */ | |
657 | static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len) | |
658 | { | |
659 | if (!uuid_prefix) | |
660 | return 1; | |
661 | ||
662 | if (!strncmp(uuid, uuid_prefix, uuid_prefix_len)) | |
663 | return 1; | |
664 | ||
665 | /* Handle transition: active device uuids might be missing the prefix */ | |
666 | if (uuid_prefix_len <= 4) | |
667 | return 0; | |
668 | ||
87f98002 | 669 | if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) |
872dea04 AK |
670 | return 0; |
671 | ||
87f98002 | 672 | if (strncmp(uuid_prefix, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) |
2b69db1f AK |
673 | return 0; |
674 | ||
87f98002 | 675 | if (!strncmp(uuid, uuid_prefix + sizeof(UUID_PREFIX) - 1, uuid_prefix_len - (sizeof(UUID_PREFIX) - 1))) |
2b69db1f AK |
676 | return 1; |
677 | ||
678 | return 0; | |
679 | } | |
680 | ||
690a5da2 AK |
681 | /* |
682 | * Returns 1 if no children. | |
683 | */ | |
b4f1578f | 684 | static int _children_suspended(struct dm_tree_node *node, |
690a5da2 AK |
685 | uint32_t inverted, |
686 | const char *uuid_prefix, | |
687 | size_t uuid_prefix_len) | |
688 | { | |
689 | struct list *list; | |
b4f1578f | 690 | struct dm_tree_link *dlink; |
690a5da2 AK |
691 | const struct dm_info *dinfo; |
692 | const char *uuid; | |
693 | ||
694 | if (inverted) { | |
b4f1578f | 695 | if (_nodes_are_linked(&node->dtree->root, node)) |
690a5da2 AK |
696 | return 1; |
697 | list = &node->used_by; | |
698 | } else { | |
b4f1578f | 699 | if (_nodes_are_linked(node, &node->dtree->root)) |
690a5da2 AK |
700 | return 1; |
701 | list = &node->uses; | |
702 | } | |
703 | ||
704 | list_iterate_items(dlink, list) { | |
b4f1578f | 705 | if (!(uuid = dm_tree_node_get_uuid(dlink->node))) { |
690a5da2 AK |
706 | stack; |
707 | continue; | |
708 | } | |
709 | ||
710 | /* Ignore if it doesn't belong to this VG */ | |
2b69db1f | 711 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
690a5da2 AK |
712 | continue; |
713 | ||
b4f1578f AK |
714 | if (!(dinfo = dm_tree_node_get_info(dlink->node))) { |
715 | stack; /* FIXME Is this normal? */ | |
690a5da2 AK |
716 | return 0; |
717 | } | |
718 | ||
719 | if (!dinfo->suspended) | |
720 | return 0; | |
721 | } | |
722 | ||
723 | return 1; | |
724 | } | |
725 | ||
3d0480ed AK |
726 | /* |
727 | * Set major and minor to zero for root of tree. | |
728 | */ | |
b4f1578f | 729 | struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree, |
3d0480ed AK |
730 | uint32_t major, |
731 | uint32_t minor) | |
732 | { | |
733 | if (!major && !minor) | |
b4f1578f | 734 | return &dtree->root; |
3d0480ed | 735 | |
b4f1578f | 736 | return _find_dm_tree_node(dtree, major, minor); |
3d0480ed AK |
737 | } |
738 | ||
165e4a11 AK |
739 | /* |
740 | * Set uuid to NULL for root of tree. | |
741 | */ | |
b4f1578f | 742 | struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *dtree, |
165e4a11 AK |
743 | const char *uuid) |
744 | { | |
745 | if (!uuid || !*uuid) | |
b4f1578f | 746 | return &dtree->root; |
165e4a11 | 747 | |
b4f1578f | 748 | return _find_dm_tree_node_by_uuid(dtree, uuid); |
165e4a11 AK |
749 | } |
750 | ||
3d0480ed AK |
751 | /* |
752 | * First time set *handle to NULL. | |
753 | * Set inverted to invert the tree. | |
754 | */ | |
b4f1578f AK |
755 | struct dm_tree_node *dm_tree_next_child(void **handle, |
756 | struct dm_tree_node *parent, | |
3d0480ed AK |
757 | uint32_t inverted) |
758 | { | |
759 | struct list **dlink = (struct list **) handle; | |
760 | struct list *use_list; | |
761 | ||
762 | if (inverted) | |
763 | use_list = &parent->used_by; | |
764 | else | |
765 | use_list = &parent->uses; | |
766 | ||
767 | if (!*dlink) | |
768 | *dlink = list_first(use_list); | |
769 | else | |
770 | *dlink = list_next(use_list, *dlink); | |
771 | ||
b4f1578f | 772 | return (*dlink) ? list_item(*dlink, struct dm_tree_link)->node : NULL; |
3d0480ed AK |
773 | } |
774 | ||
3e8c6b73 | 775 | /* |
a6d97ede | 776 | * Deactivate a device with its dependencies if the uuid prefix matches. |
3e8c6b73 | 777 | */ |
db208f51 AK |
778 | static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count, |
779 | struct dm_info *info) | |
3e8c6b73 AK |
780 | { |
781 | struct dm_task *dmt; | |
782 | int r; | |
783 | ||
784 | if (!(dmt = dm_task_create(DM_DEVICE_INFO))) { | |
785 | log_error("_info_by_dev: dm_task creation failed"); | |
786 | return 0; | |
787 | } | |
788 | ||
789 | if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) { | |
790 | log_error("_info_by_dev: Failed to set device number"); | |
791 | dm_task_destroy(dmt); | |
792 | return 0; | |
793 | } | |
794 | ||
db208f51 AK |
795 | if (!with_open_count && !dm_task_no_open_count(dmt)) |
796 | log_error("Failed to disable open_count"); | |
797 | ||
3e8c6b73 AK |
798 | if ((r = dm_task_run(dmt))) |
799 | r = dm_task_get_info(dmt, info); | |
800 | ||
801 | dm_task_destroy(dmt); | |
802 | ||
803 | return r; | |
804 | } | |
805 | ||
806 | static int _deactivate_node(const char *name, uint32_t major, uint32_t minor) | |
807 | { | |
808 | struct dm_task *dmt; | |
809 | int r; | |
810 | ||
811 | log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor); | |
812 | ||
813 | if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) { | |
814 | log_error("Deactivation dm_task creation failed for %s", name); | |
815 | return 0; | |
816 | } | |
817 | ||
818 | if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) { | |
819 | log_error("Failed to set device number for %s deactivation", name); | |
820 | dm_task_destroy(dmt); | |
821 | return 0; | |
822 | } | |
823 | ||
824 | if (!dm_task_no_open_count(dmt)) | |
825 | log_error("Failed to disable open_count"); | |
826 | ||
827 | r = dm_task_run(dmt); | |
828 | ||
165e4a11 AK |
829 | /* FIXME Until kernel returns actual name so dm-ioctl.c can handle it */ |
830 | rm_dev_node(name); | |
831 | ||
db208f51 AK |
832 | /* FIXME Remove node from tree or mark invalid? */ |
833 | ||
834 | dm_task_destroy(dmt); | |
835 | ||
836 | return r; | |
837 | } | |
838 | ||
165e4a11 AK |
839 | static int _rename_node(const char *old_name, const char *new_name, uint32_t major, uint32_t minor) |
840 | { | |
841 | struct dm_task *dmt; | |
842 | int r = 0; | |
843 | ||
844 | log_verbose("Renaming %s (%" PRIu32 ":%" PRIu32 ") to %s", old_name, major, minor, new_name); | |
845 | ||
846 | if (!(dmt = dm_task_create(DM_DEVICE_RENAME))) { | |
847 | log_error("Rename dm_task creation failed for %s", old_name); | |
848 | return 0; | |
849 | } | |
850 | ||
851 | if (!dm_task_set_name(dmt, old_name)) { | |
852 | log_error("Failed to set name for %s rename.", old_name); | |
853 | goto out; | |
854 | } | |
855 | ||
b4f1578f AK |
856 | if (!dm_task_set_newname(dmt, new_name)) |
857 | goto_out; | |
165e4a11 AK |
858 | |
859 | if (!dm_task_no_open_count(dmt)) | |
860 | log_error("Failed to disable open_count"); | |
861 | ||
862 | r = dm_task_run(dmt); | |
863 | ||
864 | out: | |
865 | dm_task_destroy(dmt); | |
866 | ||
867 | return r; | |
868 | } | |
869 | ||
165e4a11 AK |
870 | /* FIXME Merge with _suspend_node? */ |
871 | static int _resume_node(const char *name, uint32_t major, uint32_t minor, | |
872 | struct dm_info *newinfo) | |
873 | { | |
874 | struct dm_task *dmt; | |
875 | int r; | |
876 | ||
877 | log_verbose("Resuming %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor); | |
878 | ||
879 | if (!(dmt = dm_task_create(DM_DEVICE_RESUME))) { | |
880 | log_error("Suspend dm_task creation failed for %s", name); | |
881 | return 0; | |
882 | } | |
883 | ||
884 | if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) { | |
885 | log_error("Failed to set device number for %s resumption.", name); | |
886 | dm_task_destroy(dmt); | |
887 | return 0; | |
888 | } | |
889 | ||
890 | if (!dm_task_no_open_count(dmt)) | |
891 | log_error("Failed to disable open_count"); | |
892 | ||
893 | if ((r = dm_task_run(dmt))) | |
894 | r = dm_task_get_info(dmt, newinfo); | |
895 | ||
896 | dm_task_destroy(dmt); | |
897 | ||
898 | return r; | |
899 | } | |
900 | ||
db208f51 AK |
901 | static int _suspend_node(const char *name, uint32_t major, uint32_t minor, |
902 | struct dm_info *newinfo) | |
903 | { | |
904 | struct dm_task *dmt; | |
905 | int r; | |
906 | ||
907 | log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor); | |
908 | ||
909 | if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) { | |
910 | log_error("Suspend dm_task creation failed for %s", name); | |
911 | return 0; | |
912 | } | |
913 | ||
914 | if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) { | |
915 | log_error("Failed to set device number for %s suspension.", name); | |
916 | dm_task_destroy(dmt); | |
917 | return 0; | |
918 | } | |
919 | ||
920 | if (!dm_task_no_open_count(dmt)) | |
921 | log_error("Failed to disable open_count"); | |
922 | ||
923 | if ((r = dm_task_run(dmt))) | |
924 | r = dm_task_get_info(dmt, newinfo); | |
925 | ||
3e8c6b73 AK |
926 | dm_task_destroy(dmt); |
927 | ||
928 | return r; | |
929 | } | |
930 | ||
b4f1578f | 931 | int dm_tree_deactivate_children(struct dm_tree_node *dnode, |
a6d97ede AK |
932 | const char *uuid_prefix, |
933 | size_t uuid_prefix_len) | |
3e8c6b73 AK |
934 | { |
935 | void *handle = NULL; | |
b4f1578f | 936 | struct dm_tree_node *child = dnode; |
3e8c6b73 AK |
937 | struct dm_info info; |
938 | const struct dm_info *dinfo; | |
939 | const char *name; | |
940 | const char *uuid; | |
941 | ||
b4f1578f AK |
942 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
943 | if (!(dinfo = dm_tree_node_get_info(child))) { | |
3e8c6b73 AK |
944 | stack; |
945 | continue; | |
946 | } | |
947 | ||
b4f1578f | 948 | if (!(name = dm_tree_node_get_name(child))) { |
3e8c6b73 AK |
949 | stack; |
950 | continue; | |
951 | } | |
952 | ||
b4f1578f | 953 | if (!(uuid = dm_tree_node_get_uuid(child))) { |
3e8c6b73 AK |
954 | stack; |
955 | continue; | |
956 | } | |
957 | ||
958 | /* Ignore if it doesn't belong to this VG */ | |
2b69db1f | 959 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
3e8c6b73 | 960 | continue; |
3e8c6b73 AK |
961 | |
962 | /* Refresh open_count */ | |
db208f51 | 963 | if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) || |
3e8c6b73 AK |
964 | !info.exists || info.open_count) |
965 | continue; | |
966 | ||
967 | if (!_deactivate_node(name, info.major, info.minor)) { | |
968 | log_error("Unable to deactivate %s (%" PRIu32 | |
969 | ":%" PRIu32 ")", name, info.major, | |
970 | info.minor); | |
971 | continue; | |
972 | } | |
973 | ||
b4f1578f AK |
974 | if (dm_tree_node_num_children(child, 0)) |
975 | dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len); | |
3e8c6b73 AK |
976 | } |
977 | ||
978 | return 1; | |
979 | } | |
db208f51 | 980 | |
b4f1578f | 981 | int dm_tree_suspend_children(struct dm_tree_node *dnode, |
db208f51 AK |
982 | const char *uuid_prefix, |
983 | size_t uuid_prefix_len) | |
984 | { | |
985 | void *handle = NULL; | |
b4f1578f | 986 | struct dm_tree_node *child = dnode; |
db208f51 AK |
987 | struct dm_info info, newinfo; |
988 | const struct dm_info *dinfo; | |
989 | const char *name; | |
990 | const char *uuid; | |
991 | ||
690a5da2 | 992 | /* Suspend nodes at this level of the tree */ |
b4f1578f AK |
993 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
994 | if (!(dinfo = dm_tree_node_get_info(child))) { | |
db208f51 AK |
995 | stack; |
996 | continue; | |
997 | } | |
998 | ||
b4f1578f | 999 | if (!(name = dm_tree_node_get_name(child))) { |
db208f51 AK |
1000 | stack; |
1001 | continue; | |
1002 | } | |
1003 | ||
b4f1578f | 1004 | if (!(uuid = dm_tree_node_get_uuid(child))) { |
db208f51 AK |
1005 | stack; |
1006 | continue; | |
1007 | } | |
1008 | ||
1009 | /* Ignore if it doesn't belong to this VG */ | |
2b69db1f | 1010 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
db208f51 AK |
1011 | continue; |
1012 | ||
690a5da2 AK |
1013 | /* Ensure immediate parents are already suspended */ |
1014 | if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len)) | |
1015 | continue; | |
1016 | ||
db208f51 AK |
1017 | if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) || |
1018 | !info.exists) | |
1019 | continue; | |
1020 | ||
1021 | if (!_suspend_node(name, info.major, info.minor, &newinfo)) { | |
1022 | log_error("Unable to suspend %s (%" PRIu32 | |
1023 | ":%" PRIu32 ")", name, info.major, | |
1024 | info.minor); | |
1025 | continue; | |
1026 | } | |
1027 | ||
1028 | /* Update cached info */ | |
1029 | child->info = newinfo; | |
690a5da2 AK |
1030 | } |
1031 | ||
1032 | /* Then suspend any child nodes */ | |
1033 | handle = NULL; | |
1034 | ||
b4f1578f AK |
1035 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
1036 | if (!(uuid = dm_tree_node_get_uuid(child))) { | |
690a5da2 AK |
1037 | stack; |
1038 | continue; | |
1039 | } | |
1040 | ||
1041 | /* Ignore if it doesn't belong to this VG */ | |
87f98002 | 1042 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
690a5da2 | 1043 | continue; |
db208f51 | 1044 | |
b4f1578f AK |
1045 | if (dm_tree_node_num_children(child, 0)) |
1046 | dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len); | |
db208f51 AK |
1047 | } |
1048 | ||
1049 | return 1; | |
1050 | } | |
1051 | ||
b4f1578f | 1052 | int dm_tree_activate_children(struct dm_tree_node *dnode, |
db208f51 AK |
1053 | const char *uuid_prefix, |
1054 | size_t uuid_prefix_len) | |
1055 | { | |
1056 | void *handle = NULL; | |
b4f1578f | 1057 | struct dm_tree_node *child = dnode; |
165e4a11 AK |
1058 | struct dm_info newinfo; |
1059 | const char *name; | |
db208f51 | 1060 | const char *uuid; |
56c28292 | 1061 | int priority; |
db208f51 | 1062 | |
165e4a11 | 1063 | /* Activate children first */ |
b4f1578f AK |
1064 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
1065 | if (!(uuid = dm_tree_node_get_uuid(child))) { | |
165e4a11 AK |
1066 | stack; |
1067 | continue; | |
db208f51 AK |
1068 | } |
1069 | ||
908db078 AK |
1070 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
1071 | continue; | |
db208f51 | 1072 | |
b4f1578f AK |
1073 | if (dm_tree_node_num_children(child, 0)) |
1074 | dm_tree_activate_children(child, uuid_prefix, uuid_prefix_len); | |
56c28292 | 1075 | } |
165e4a11 | 1076 | |
56c28292 | 1077 | handle = NULL; |
165e4a11 | 1078 | |
56c28292 AK |
1079 | for (priority = 0; priority < 2; priority++) { |
1080 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { | |
1081 | if (!(uuid = dm_tree_node_get_uuid(child))) { | |
1082 | stack; | |
1083 | continue; | |
165e4a11 | 1084 | } |
165e4a11 | 1085 | |
56c28292 AK |
1086 | if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
1087 | continue; | |
165e4a11 | 1088 | |
56c28292 AK |
1089 | if (priority != child->activation_priority) |
1090 | continue; | |
165e4a11 | 1091 | |
56c28292 AK |
1092 | if (!(name = dm_tree_node_get_name(child))) { |
1093 | stack; | |
1094 | continue; | |
1095 | } | |
1096 | ||
1097 | /* Rename? */ | |
1098 | if (child->props.new_name) { | |
1099 | if (!_rename_node(name, child->props.new_name, child->info.major, child->info.minor)) { | |
1100 | log_error("Failed to rename %s (%" PRIu32 | |
1101 | ":%" PRIu32 ") to %s", name, child->info.major, | |
1102 | child->info.minor, child->props.new_name); | |
1103 | return 0; | |
1104 | } | |
1105 | child->name = child->props.new_name; | |
1106 | child->props.new_name = NULL; | |
1107 | } | |
1108 | ||
1109 | if (!child->info.inactive_table && !child->info.suspended) | |
1110 | continue; | |
1111 | ||
1112 | if (!_resume_node(name, child->info.major, child->info.minor, &newinfo)) { | |
1113 | log_error("Unable to resume %s (%" PRIu32 | |
1114 | ":%" PRIu32 ")", name, child->info.major, | |
1115 | child->info.minor); | |
1116 | continue; | |
1117 | } | |
1118 | ||
1119 | /* Update cached info */ | |
1120 | child->info = newinfo; | |
1121 | } | |
db208f51 AK |
1122 | } |
1123 | ||
165e4a11 AK |
1124 | handle = NULL; |
1125 | ||
1126 | return 1; | |
1127 | } | |
1128 | ||
b4f1578f | 1129 | static int _create_node(struct dm_tree_node *dnode) |
165e4a11 AK |
1130 | { |
1131 | int r = 0; | |
1132 | struct dm_task *dmt; | |
1133 | ||
1134 | log_verbose("Creating %s", dnode->name); | |
1135 | ||
1136 | if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) { | |
1137 | log_error("Create dm_task creation failed for %s", dnode->name); | |
1138 | return 0; | |
1139 | } | |
1140 | ||
1141 | if (!dm_task_set_name(dmt, dnode->name)) { | |
1142 | log_error("Failed to set device name for %s", dnode->name); | |
1143 | goto out; | |
1144 | } | |
1145 | ||
1146 | if (!dm_task_set_uuid(dmt, dnode->uuid)) { | |
1147 | log_error("Failed to set uuid for %s", dnode->name); | |
1148 | goto out; | |
1149 | } | |
1150 | ||
1151 | if (dnode->props.major && | |
1152 | (!dm_task_set_major(dmt, dnode->props.major) || | |
1153 | !dm_task_set_minor(dmt, dnode->props.minor))) { | |
1154 | log_error("Failed to set device number for %s creation.", dnode->name); | |
1155 | goto out; | |
1156 | } | |
1157 | ||
1158 | if (dnode->props.read_only && !dm_task_set_ro(dmt)) { | |
1159 | log_error("Failed to set read only flag for %s", dnode->name); | |
1160 | goto out; | |
1161 | } | |
1162 | ||
1163 | if (!dm_task_no_open_count(dmt)) | |
1164 | log_error("Failed to disable open_count"); | |
1165 | ||
1166 | if ((r = dm_task_run(dmt))) | |
1167 | r = dm_task_get_info(dmt, &dnode->info); | |
1168 | ||
1169 | out: | |
1170 | dm_task_destroy(dmt); | |
1171 | ||
1172 | return r; | |
1173 | } | |
1174 | ||
1175 | ||
b4f1578f | 1176 | static int _build_dev_string(char *devbuf, size_t bufsize, struct dm_tree_node *node) |
165e4a11 AK |
1177 | { |
1178 | if (!dm_format_dev(devbuf, bufsize, node->info.major, node->info.minor)) { | |
1179 | log_error("Failed to format %s device number for %s as dm " | |
1180 | "target (%u,%u)", | |
1181 | node->name, node->uuid, node->info.major, node->info.minor); | |
1182 | return 0; | |
1183 | } | |
1184 | ||
1185 | return 1; | |
1186 | } | |
1187 | ||
1188 | static int _emit_areas_line(struct dm_task *dmt, struct load_segment *seg, char *params, size_t paramsize, int *pos) | |
1189 | { | |
1190 | struct seg_area *area; | |
1191 | char devbuf[10]; | |
1192 | int tw; | |
1193 | const char *prefix = ""; | |
1194 | ||
1195 | list_iterate_items(area, &seg->areas) { | |
b4f1578f AK |
1196 | if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node)) |
1197 | return_0; | |
165e4a11 AK |
1198 | |
1199 | if ((tw = _dm_snprintf(params + *pos, paramsize - *pos, "%s%s %" PRIu64, | |
1200 | prefix, devbuf, area->offset)) < 0) { | |
b4f1578f | 1201 | stack; /* Out of space */ |
165e4a11 AK |
1202 | return -1; |
1203 | } | |
1204 | ||
1205 | prefix = " "; | |
1206 | *pos += tw; | |
1207 | } | |
1208 | ||
1209 | return 1; | |
1210 | } | |
1211 | ||
1212 | static int _emit_segment_line(struct dm_task *dmt, struct load_segment *seg, uint64_t *seg_start, char *params, size_t paramsize) | |
1213 | { | |
1214 | int pos = 0; | |
1215 | int tw; | |
1216 | int r; | |
1217 | char originbuf[10], cowbuf[10], logbuf[10]; | |
1218 | ||
1219 | switch(seg->type) { | |
1220 | case SEG_ERROR: | |
1221 | case SEG_ZERO: | |
1222 | params[0] = '\0'; | |
1223 | case SEG_LINEAR: | |
1224 | break; | |
1225 | case SEG_MIRRORED: | |
1226 | if (seg->clustered) { | |
1227 | if ((tw = _dm_snprintf(params + pos, paramsize - pos, "clustered ")) < 0) { | |
b4f1578f | 1228 | stack; /* Out of space */ |
165e4a11 AK |
1229 | return -1; |
1230 | } | |
1231 | pos += tw; | |
1232 | } | |
1233 | if (!seg->log) { | |
1234 | if ((tw = _dm_snprintf(params + pos, paramsize - pos, "core 1 ")) < 0) { | |
b4f1578f | 1235 | stack; /* Out of space */ |
165e4a11 AK |
1236 | return -1; |
1237 | } | |
1238 | pos += tw; | |
1239 | } else { | |
b4f1578f AK |
1240 | if (!_build_dev_string(logbuf, sizeof(logbuf), seg->log)) |
1241 | return_0; | |
165e4a11 | 1242 | if ((tw = _dm_snprintf(params + pos, paramsize - pos, "disk 2 %s ", logbuf)) < 0) { |
b4f1578f | 1243 | stack; /* Out of space */ |
165e4a11 AK |
1244 | return -1; |
1245 | } | |
1246 | pos += tw; | |
1247 | } | |
1248 | if ((tw = _dm_snprintf(params + pos, paramsize - pos, "%u %u ", seg->region_size, seg->mirror_area_count)) < 0) { | |
b4f1578f | 1249 | stack; /* Out of space */ |
165e4a11 AK |
1250 | return -1; |
1251 | } | |
1252 | pos += tw; | |
1253 | break; | |
1254 | case SEG_SNAPSHOT: | |
b4f1578f AK |
1255 | if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin)) |
1256 | return_0; | |
1257 | if (!_build_dev_string(cowbuf, sizeof(cowbuf), seg->cow)) | |
1258 | return_0; | |
165e4a11 AK |
1259 | if ((pos = _dm_snprintf(params, paramsize, "%s %s %c %d", |
1260 | originbuf, cowbuf, | |
1261 | seg->persistent ? 'P' : 'N', | |
1262 | seg->chunk_size)) < 0) { | |
b4f1578f | 1263 | stack; /* Out of space */ |
165e4a11 AK |
1264 | return -1; |
1265 | } | |
1266 | break; | |
1267 | case SEG_SNAPSHOT_ORIGIN: | |
b4f1578f AK |
1268 | if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin)) |
1269 | return_0; | |
165e4a11 AK |
1270 | if ((pos = _dm_snprintf(params, paramsize, "%s", |
1271 | originbuf)) < 0) { | |
b4f1578f | 1272 | stack; /* Out of space */ |
165e4a11 AK |
1273 | return -1; |
1274 | } | |
1275 | break; | |
1276 | case SEG_STRIPED: | |
1277 | if ((pos = _dm_snprintf(params, paramsize, "%u %u ", | |
1278 | seg->area_count, | |
1279 | seg->stripe_size)) < 0) { | |
b4f1578f | 1280 | stack; /* Out of space */ |
165e4a11 AK |
1281 | return -1; |
1282 | } | |
1283 | break; | |
1284 | } | |
1285 | ||
1286 | switch(seg->type) { | |
1287 | case SEG_ERROR: | |
1288 | case SEG_SNAPSHOT: | |
1289 | case SEG_SNAPSHOT_ORIGIN: | |
1290 | case SEG_ZERO: | |
1291 | break; | |
1292 | case SEG_LINEAR: | |
1293 | case SEG_MIRRORED: | |
1294 | case SEG_STRIPED: | |
1295 | if ((r = _emit_areas_line(dmt, seg, params, paramsize, &pos)) <= 0) { | |
1296 | stack; | |
1297 | return r; | |
1298 | } | |
1299 | break; | |
1300 | } | |
1301 | ||
1302 | log_debug("Adding target: %" PRIu64 " %" PRIu64 " %s %s", | |
1303 | *seg_start, seg->size, dm_segtypes[seg->type].target, params); | |
1304 | ||
b4f1578f AK |
1305 | if (!dm_task_add_target(dmt, *seg_start, seg->size, dm_segtypes[seg->type].target, params)) |
1306 | return_0; | |
165e4a11 AK |
1307 | |
1308 | *seg_start += seg->size; | |
1309 | ||
1310 | return 1; | |
1311 | } | |
1312 | ||
1313 | static int _emit_segment(struct dm_task *dmt, struct load_segment *seg, | |
1314 | uint64_t *seg_start) | |
1315 | { | |
1316 | char *params; | |
1317 | size_t paramsize = 4096; | |
1318 | int ret; | |
1319 | ||
1320 | do { | |
1321 | if (!(params = dm_malloc(paramsize))) { | |
1322 | log_error("Insufficient space for target parameters."); | |
1323 | return 0; | |
1324 | } | |
1325 | ||
1326 | ret = _emit_segment_line(dmt, seg, seg_start, params, paramsize); | |
1327 | dm_free(params); | |
1328 | ||
1329 | if (!ret) | |
1330 | stack; | |
1331 | ||
1332 | if (ret >= 0) | |
1333 | return ret; | |
1334 | ||
1335 | log_debug("Insufficient space in params[%" PRIsize_t | |
1336 | "] for target parameters.", paramsize); | |
1337 | ||
1338 | paramsize *= 2; | |
1339 | } while (paramsize < MAX_TARGET_PARAMSIZE); | |
1340 | ||
1341 | log_error("Target parameter size too big. Aborting."); | |
1342 | return 0; | |
1343 | } | |
1344 | ||
b4f1578f | 1345 | static int _load_node(struct dm_tree_node *dnode) |
165e4a11 AK |
1346 | { |
1347 | int r = 0; | |
1348 | struct dm_task *dmt; | |
1349 | struct load_segment *seg; | |
1350 | uint64_t seg_start = 0; | |
1351 | ||
1352 | log_verbose("Loading %s table", dnode->name); | |
1353 | ||
1354 | if (!(dmt = dm_task_create(DM_DEVICE_RELOAD))) { | |
1355 | log_error("Reload dm_task creation failed for %s", dnode->name); | |
1356 | return 0; | |
1357 | } | |
1358 | ||
1359 | if (!dm_task_set_major(dmt, dnode->info.major) || | |
1360 | !dm_task_set_minor(dmt, dnode->info.minor)) { | |
1361 | log_error("Failed to set device number for %s reload.", dnode->name); | |
1362 | goto out; | |
1363 | } | |
1364 | ||
1365 | if (dnode->props.read_only && !dm_task_set_ro(dmt)) { | |
1366 | log_error("Failed to set read only flag for %s", dnode->name); | |
1367 | goto out; | |
1368 | } | |
1369 | ||
1370 | if (!dm_task_no_open_count(dmt)) | |
1371 | log_error("Failed to disable open_count"); | |
1372 | ||
1373 | list_iterate_items(seg, &dnode->props.segs) | |
b4f1578f AK |
1374 | if (!_emit_segment(dmt, seg, &seg_start)) |
1375 | goto_out; | |
165e4a11 | 1376 | |
ec289b64 AK |
1377 | if (!dm_task_suppress_identical_reload(dmt)) |
1378 | log_error("Failed to suppress reload of identical tables."); | |
1379 | ||
1380 | if ((r = dm_task_run(dmt))) { | |
165e4a11 | 1381 | r = dm_task_get_info(dmt, &dnode->info); |
ec289b64 AK |
1382 | if (r && !dnode->info.inactive_table) |
1383 | log_verbose("Suppressed %s identical table reload.", | |
1384 | dnode->name); | |
1385 | } | |
165e4a11 AK |
1386 | |
1387 | dnode->props.segment_count = 0; | |
1388 | ||
1389 | out: | |
1390 | dm_task_destroy(dmt); | |
1391 | ||
1392 | return r; | |
165e4a11 AK |
1393 | } |
1394 | ||
b4f1578f | 1395 | int dm_tree_preload_children(struct dm_tree_node *dnode, |
165e4a11 | 1396 | const char *uuid_prefix, |
e6a6954e | 1397 | size_t uuid_prefix_len) |
165e4a11 AK |
1398 | { |
1399 | void *handle = NULL; | |
b4f1578f | 1400 | struct dm_tree_node *child; |
165e4a11 AK |
1401 | struct dm_info newinfo; |
1402 | const char *name; | |
1403 | ||
1404 | /* Preload children first */ | |
b4f1578f | 1405 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
165e4a11 AK |
1406 | /* Skip existing non-device-mapper devices */ |
1407 | if (!child->info.exists && child->info.major) | |
1408 | continue; | |
1409 | ||
1410 | /* Ignore if it doesn't belong to this VG */ | |
87f98002 AK |
1411 | if (child->info.exists && |
1412 | !_uuid_prefix_matches(child->uuid, uuid_prefix, uuid_prefix_len)) | |
165e4a11 AK |
1413 | continue; |
1414 | ||
b4f1578f | 1415 | if (dm_tree_node_num_children(child, 0)) |
e6a6954e | 1416 | dm_tree_preload_children(child, uuid_prefix, uuid_prefix_len); |
165e4a11 | 1417 | |
b4f1578f | 1418 | if (!(name = dm_tree_node_get_name(child))) { |
165e4a11 AK |
1419 | stack; |
1420 | continue; | |
1421 | } | |
1422 | ||
1423 | /* FIXME Cope if name exists with no uuid? */ | |
1424 | if (!child->info.exists) { | |
1425 | if (!_create_node(child)) { | |
1426 | stack; | |
1427 | return 0; | |
1428 | } | |
1429 | } | |
1430 | ||
1431 | if (!child->info.inactive_table && child->props.segment_count) { | |
1432 | if (!_load_node(child)) { | |
1433 | stack; | |
1434 | return 0; | |
1435 | } | |
1436 | } | |
1437 | ||
1438 | /* Resume device immediately if it has parents */ | |
abbca212 | 1439 | if (!dm_tree_node_num_children(child, 1)) |
165e4a11 AK |
1440 | continue; |
1441 | ||
7707ea90 AK |
1442 | if (!child->info.inactive_table && !child->info.suspended) |
1443 | continue; | |
1444 | ||
165e4a11 AK |
1445 | if (!_resume_node(name, child->info.major, child->info.minor, &newinfo)) { |
1446 | log_error("Unable to resume %s (%" PRIu32 | |
1447 | ":%" PRIu32 ")", name, child->info.major, | |
1448 | child->info.minor); | |
1449 | continue; | |
1450 | } | |
1451 | ||
1452 | /* Update cached info */ | |
1453 | child->info = newinfo; | |
1454 | } | |
1455 | ||
1456 | handle = NULL; | |
1457 | ||
1458 | return 1; | |
1459 | } | |
1460 | ||
165e4a11 AK |
1461 | /* |
1462 | * Returns 1 if unsure. | |
1463 | */ | |
b4f1578f | 1464 | int dm_tree_children_use_uuid(struct dm_tree_node *dnode, |
165e4a11 AK |
1465 | const char *uuid_prefix, |
1466 | size_t uuid_prefix_len) | |
1467 | { | |
1468 | void *handle = NULL; | |
b4f1578f | 1469 | struct dm_tree_node *child = dnode; |
165e4a11 AK |
1470 | const char *uuid; |
1471 | ||
b4f1578f AK |
1472 | while ((child = dm_tree_next_child(&handle, dnode, 0))) { |
1473 | if (!(uuid = dm_tree_node_get_uuid(child))) { | |
1474 | log_error("Failed to get uuid for dtree node."); | |
165e4a11 AK |
1475 | return 1; |
1476 | } | |
1477 | ||
87f98002 | 1478 | if (_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len)) |
165e4a11 AK |
1479 | return 1; |
1480 | ||
b4f1578f AK |
1481 | if (dm_tree_node_num_children(child, 0)) |
1482 | dm_tree_children_use_uuid(child, uuid_prefix, uuid_prefix_len); | |
165e4a11 AK |
1483 | } |
1484 | ||
1485 | return 0; | |
1486 | } | |
1487 | ||
1488 | /* | |
1489 | * Target functions | |
1490 | */ | |
b4f1578f | 1491 | static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned type, uint64_t size) |
165e4a11 AK |
1492 | { |
1493 | struct load_segment *seg; | |
1494 | ||
b4f1578f AK |
1495 | if (!(seg = dm_pool_zalloc(dnode->dtree->mem, sizeof(*seg)))) { |
1496 | log_error("dtree node segment allocation failed"); | |
165e4a11 AK |
1497 | return NULL; |
1498 | } | |
1499 | ||
1500 | seg->type = type; | |
1501 | seg->size = size; | |
1502 | seg->area_count = 0; | |
1503 | list_init(&seg->areas); | |
1504 | seg->stripe_size = 0; | |
1505 | seg->persistent = 0; | |
1506 | seg->chunk_size = 0; | |
1507 | seg->cow = NULL; | |
1508 | seg->origin = NULL; | |
1509 | ||
1510 | list_add(&dnode->props.segs, &seg->list); | |
1511 | dnode->props.segment_count++; | |
1512 | ||
1513 | return seg; | |
1514 | } | |
1515 | ||
b4f1578f | 1516 | int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode, |
165e4a11 AK |
1517 | uint64_t size, |
1518 | const char *origin_uuid) | |
1519 | { | |
1520 | struct load_segment *seg; | |
b4f1578f | 1521 | struct dm_tree_node *origin_node; |
165e4a11 | 1522 | |
b4f1578f AK |
1523 | if (!(seg = _add_segment(dnode, SEG_SNAPSHOT_ORIGIN, size))) |
1524 | return_0; | |
165e4a11 | 1525 | |
b4f1578f | 1526 | if (!(origin_node = dm_tree_find_node_by_uuid(dnode->dtree, origin_uuid))) { |
165e4a11 AK |
1527 | log_error("Couldn't find snapshot origin uuid %s.", origin_uuid); |
1528 | return 0; | |
1529 | } | |
1530 | ||
1531 | seg->origin = origin_node; | |
b4f1578f AK |
1532 | if (!_link_tree_nodes(dnode, origin_node)) |
1533 | return_0; | |
165e4a11 | 1534 | |
56c28292 AK |
1535 | /* Resume snapshot origins after new snapshots */ |
1536 | dnode->activation_priority = 1; | |
1537 | ||
165e4a11 AK |
1538 | return 1; |
1539 | } | |
1540 | ||
b4f1578f | 1541 | int dm_tree_node_add_snapshot_target(struct dm_tree_node *node, |
165e4a11 AK |
1542 | uint64_t size, |
1543 | const char *origin_uuid, | |
1544 | const char *cow_uuid, | |
1545 | int persistent, | |
1546 | uint32_t chunk_size) | |
1547 | { | |
1548 | struct load_segment *seg; | |
b4f1578f | 1549 | struct dm_tree_node *origin_node, *cow_node; |
165e4a11 | 1550 | |
b4f1578f AK |
1551 | if (!(seg = _add_segment(node, SEG_SNAPSHOT, size))) |
1552 | return_0; | |
165e4a11 | 1553 | |
b4f1578f | 1554 | if (!(origin_node = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) { |
165e4a11 AK |
1555 | log_error("Couldn't find snapshot origin uuid %s.", origin_uuid); |
1556 | return 0; | |
1557 | } | |
1558 | ||
1559 | seg->origin = origin_node; | |
b4f1578f AK |
1560 | if (!_link_tree_nodes(node, origin_node)) |
1561 | return_0; | |
165e4a11 | 1562 | |
b4f1578f | 1563 | if (!(cow_node = dm_tree_find_node_by_uuid(node->dtree, cow_uuid))) { |
165e4a11 AK |
1564 | log_error("Couldn't find snapshot origin uuid %s.", cow_uuid); |
1565 | return 0; | |
1566 | } | |
1567 | ||
1568 | seg->cow = cow_node; | |
b4f1578f AK |
1569 | if (!_link_tree_nodes(node, cow_node)) |
1570 | return_0; | |
165e4a11 AK |
1571 | |
1572 | seg->persistent = persistent ? 1 : 0; | |
1573 | seg->chunk_size = chunk_size; | |
1574 | ||
1575 | return 1; | |
1576 | } | |
1577 | ||
b4f1578f | 1578 | int dm_tree_node_add_error_target(struct dm_tree_node *node, |
165e4a11 AK |
1579 | uint64_t size) |
1580 | { | |
b4f1578f AK |
1581 | if (!_add_segment(node, SEG_ERROR, size)) |
1582 | return_0; | |
165e4a11 AK |
1583 | |
1584 | return 1; | |
1585 | } | |
1586 | ||
b4f1578f | 1587 | int dm_tree_node_add_zero_target(struct dm_tree_node *node, |
165e4a11 AK |
1588 | uint64_t size) |
1589 | { | |
b4f1578f AK |
1590 | if (!_add_segment(node, SEG_ZERO, size)) |
1591 | return_0; | |
165e4a11 AK |
1592 | |
1593 | return 1; | |
1594 | } | |
1595 | ||
b4f1578f | 1596 | int dm_tree_node_add_linear_target(struct dm_tree_node *node, |
165e4a11 AK |
1597 | uint64_t size) |
1598 | { | |
b4f1578f AK |
1599 | if (!_add_segment(node, SEG_LINEAR, size)) |
1600 | return_0; | |
165e4a11 AK |
1601 | |
1602 | return 1; | |
1603 | } | |
1604 | ||
b4f1578f | 1605 | int dm_tree_node_add_striped_target(struct dm_tree_node *node, |
165e4a11 AK |
1606 | uint64_t size, |
1607 | uint32_t stripe_size) | |
1608 | { | |
1609 | struct load_segment *seg; | |
1610 | ||
b4f1578f AK |
1611 | if (!(seg = _add_segment(node, SEG_STRIPED, size))) |
1612 | return_0; | |
165e4a11 AK |
1613 | |
1614 | seg->stripe_size = stripe_size; | |
1615 | ||
1616 | return 1; | |
1617 | } | |
1618 | ||
b4f1578f | 1619 | int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node, |
165e4a11 AK |
1620 | uint32_t region_size, |
1621 | unsigned clustered, | |
1622 | const char *log_uuid, | |
1623 | unsigned area_count) | |
1624 | { | |
908db078 | 1625 | struct dm_tree_node *log_node = NULL; |
165e4a11 AK |
1626 | struct load_segment *seg; |
1627 | ||
1628 | if (!node->props.segment_count) { | |
1629 | log_error("Internal error: Attempt to add target area to missing segment."); | |
1630 | return 0; | |
1631 | } | |
1632 | ||
1633 | seg = list_item(list_last(&node->props.segs), struct load_segment); | |
1634 | ||
24b026e3 AK |
1635 | if (log_uuid) { |
1636 | if (!(log_node = dm_tree_find_node_by_uuid(node->dtree, log_uuid))) { | |
1637 | log_error("Couldn't find mirror log uuid %s.", log_uuid); | |
1638 | return 0; | |
1639 | } | |
1640 | if (!_link_tree_nodes(node, log_node)) | |
1641 | return_0; | |
165e4a11 AK |
1642 | } |
1643 | ||
1644 | seg->log = log_node; | |
165e4a11 AK |
1645 | seg->region_size = region_size; |
1646 | seg->clustered = clustered; | |
1647 | seg->mirror_area_count = area_count; | |
1648 | ||
1649 | return 1; | |
1650 | } | |
1651 | ||
b4f1578f | 1652 | int dm_tree_node_add_mirror_target(struct dm_tree_node *node, |
165e4a11 AK |
1653 | uint64_t size) |
1654 | { | |
1655 | struct load_segment *seg; | |
1656 | ||
b4f1578f AK |
1657 | if (!(seg = _add_segment(node, SEG_MIRRORED, size))) |
1658 | return_0; | |
165e4a11 AK |
1659 | |
1660 | return 1; | |
1661 | } | |
1662 | ||
b4f1578f | 1663 | static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct dm_tree_node *dev_node, uint64_t offset) |
165e4a11 AK |
1664 | { |
1665 | struct seg_area *area; | |
1666 | ||
b4f1578f | 1667 | if (!(area = dm_pool_zalloc(node->dtree->mem, sizeof (*area)))) { |
165e4a11 AK |
1668 | log_error("Failed to allocate target segment area."); |
1669 | return 0; | |
1670 | } | |
1671 | ||
1672 | area->dev_node = dev_node; | |
1673 | area->offset = offset; | |
1674 | ||
1675 | list_add(&seg->areas, &area->list); | |
1676 | seg->area_count++; | |
1677 | ||
1678 | return 1; | |
1679 | } | |
1680 | ||
b4f1578f | 1681 | int dm_tree_node_add_target_area(struct dm_tree_node *node, |
165e4a11 AK |
1682 | const char *dev_name, |
1683 | const char *uuid, | |
1684 | uint64_t offset) | |
1685 | { | |
1686 | struct load_segment *seg; | |
1687 | struct stat info; | |
b4f1578f | 1688 | struct dm_tree_node *dev_node; |
165e4a11 AK |
1689 | |
1690 | if ((!dev_name || !*dev_name) && (!uuid || !*uuid)) { | |
b4f1578f | 1691 | log_error("dm_tree_node_add_target_area called without device"); |
165e4a11 AK |
1692 | return 0; |
1693 | } | |
1694 | ||
1695 | if (uuid) { | |
b4f1578f | 1696 | if (!(dev_node = dm_tree_find_node_by_uuid(node->dtree, uuid))) { |
165e4a11 AK |
1697 | log_error("Couldn't find area uuid %s.", uuid); |
1698 | return 0; | |
1699 | } | |
b4f1578f AK |
1700 | if (!_link_tree_nodes(node, dev_node)) |
1701 | return_0; | |
165e4a11 AK |
1702 | } else { |
1703 | if (stat(dev_name, &info) < 0) { | |
1704 | log_error("Device %s not found.", dev_name); | |
1705 | return 0; | |
1706 | } | |
1707 | ||
1708 | if (!S_ISBLK(info.st_mode)) { | |
1709 | log_error("Device %s is not a block device.", dev_name); | |
1710 | return 0; | |
1711 | } | |
1712 | ||
1713 | /* FIXME Check correct macro use */ | |
b4f1578f AK |
1714 | if (!(dev_node = _add_dev(node->dtree, node, MAJOR(info.st_rdev), MINOR(info.st_rdev)))) |
1715 | return_0; | |
165e4a11 AK |
1716 | } |
1717 | ||
1718 | if (!node->props.segment_count) { | |
1719 | log_error("Internal error: Attempt to add target area to missing segment."); | |
1720 | return 0; | |
1721 | } | |
1722 | ||
1723 | seg = list_item(list_last(&node->props.segs), struct load_segment); | |
1724 | ||
b4f1578f AK |
1725 | if (!_add_area(node, seg, dev_node, offset)) |
1726 | return_0; | |
165e4a11 AK |
1727 | |
1728 | return 1; | |
db208f51 | 1729 | } |