]> git.ozlabs.org Git - petitboot/blob - discover/devmapper.c
1fd1215d91d802d495869930629dbfed37c01b49
[petitboot] / discover / devmapper.c
1 #include <talloc/talloc.h>
2 #include <types/types.h>
3 #include <log/log.h>
4 #include <errno.h>
5 #include <string.h>
6
7 #include "libdevmapper.h"
8 #include "devmapper.h"
9
10 #define MERGE_INTERVAL_US       200000
11
12 struct target {
13         long unsigned int       start_sector;
14         long unsigned int       end_sector;
15         char                    *ttype;
16         char                    *params;
17 };
18
19 /* Return the number of sectors on a block device. Zero represents an error */
20 static unsigned int get_block_sectors(struct discover_device *device)
21 {
22         const char *tmp;
23         long unsigned int sectors;
24
25         tmp = discover_device_get_param(device, "ID_PART_ENTRY_SIZE");
26         if (!tmp) {
27                 pb_debug("Could not retrieve ID_PART_ENTRY_SIZE for %s\n",
28                        device->device_path);
29                 return 0;
30         }
31
32         errno = 0;
33         sectors = strtoul(tmp, NULL, 0);
34         if (errno) {
35                 pb_debug("Error reading sector count for %s: %s\n",
36                        device->device_path, strerror(errno));
37                 sectors = 0;
38         }
39
40         return sectors;
41 }
42
43 /*
44  * The system's libdm may or may not have udev sync support. Tell libdm
45  * to manage the creation of device nodes itself rather than waiting on udev
46  * to do it
47  */
48 static inline int set_cookie(struct dm_task *task, uint32_t *cookie)
49 {
50         uint16_t udev_rules = 0;
51         *cookie = 0;
52
53         dm_udev_set_sync_support(0);
54         udev_rules |= DM_UDEV_DISABLE_DM_RULES_FLAG |
55                 DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
56
57         return dm_task_set_cookie(task, cookie, udev_rules);
58 }
59
60 static bool snapshot_merge_complete(const char *dm_name)
61 {
62         long long unsigned int sectors, meta_sectors;
63         char *params = NULL,  *target_type = NULL;
64         uint64_t start, length;
65         struct dm_task *task;
66         bool result = true;
67         int n;
68
69         task = dm_task_create(DM_DEVICE_STATUS);
70         if (!task) {
71                 pb_log("%s: Error creating task\n", __func__);
72                 return result;
73         }
74
75         if (!dm_task_set_name(task, dm_name)) {
76                 pb_log("No dm-device named '%s'\n", dm_name);
77                 goto out;
78         }
79
80         if (!dm_task_run(task)) {
81                 pb_log("Unable to retrieve status for '%s'\n", dm_name);
82                 goto out;
83         }
84
85         dm_get_next_target(task, NULL, &start, &length, &target_type, &params);
86
87         if (!params) {
88                 pb_log("Unable to retrieve params for '%s'\n", dm_name);
89                 goto out;
90         }
91
92         if (!strncmp(params, "Invalid", strlen("Invalid"))) {
93                 pb_log("dm-device %s has become invalid\n", dm_name);
94                 goto out;
95         }
96
97         /* Merge is complete when metadata sectors are the only sectors
98          * allocated - see Documentation/device-mapper/snapshot.txt */
99         n = sscanf(params, "%llu/%*u %llu", &sectors, &meta_sectors);
100         if (n != 2) {
101                 pb_log("%s unexpected status: '%s'\n", dm_name, params);
102                 goto out;
103         }
104         result = sectors == meta_sectors;
105
106         pb_debug("%s merging; %llu sectors, %llu metadata sectors\n",
107                  dm_name, sectors, meta_sectors);
108
109 out:
110         /* In case of error or an invalid snapshot return true so callers will
111          * move on and catch the error */
112         dm_task_destroy(task);
113         return result;
114 }
115
116 /* Resume or suspend dm device */
117 static int set_device_active(const char *dm_name, bool active)
118 {
119         struct dm_task *task;
120         uint32_t cookie;
121         int rc = -1;
122
123         if (active)
124                 task = dm_task_create(DM_DEVICE_RESUME);
125         else
126                 task = dm_task_create(DM_DEVICE_SUSPEND);
127
128         if (!task) {
129                 pb_log("%s: Could not create dm_task\n", __func__);
130                 return rc;
131         }
132
133         if (!dm_task_set_name(task, dm_name)) {
134                 pb_log("No dm-device named '%s'\n", dm_name);
135                 goto out;
136         }
137
138         if (!set_cookie(task, &cookie))
139                 goto out;
140
141         if (!dm_task_run(task)) {
142                 pb_log("Unable to %s device '%s'\n",
143                        active ? "resume" : "suspend", dm_name);
144                 goto out;
145         }
146
147         rc = 0;
148
149         /* Wait for /dev/mapper/ entries to be updated */
150         dm_udev_wait(cookie);
151
152 out:
153         dm_task_destroy(task);
154         return rc;
155 }
156
157 /* Run a DM_DEVICE_CREATE task with provided table (ttype and params) */
158 static int run_create_task(const char *dm_name, const struct target *target)
159 {
160         struct dm_task *task;
161         uint32_t cookie;
162
163         pb_debug("%s: %lu %lu '%s' '%s'\n", __func__,
164                  target->start_sector, target->end_sector,
165                  target->ttype, target->params);
166
167         task = dm_task_create(DM_DEVICE_CREATE);
168         if (!task) {
169                 pb_log("Error creating new dm-task\n");
170                 return -1;
171         }
172
173         if (!dm_task_set_name(task, dm_name))
174                 return -1;
175
176         if (!dm_task_add_target(task, target->start_sector, target->end_sector,
177                                 target->ttype, target->params))
178                 return -1;
179
180         if (!dm_task_set_add_node(task, DM_ADD_NODE_ON_CREATE))
181                 return -1;
182
183         if (!set_cookie(task, &cookie))
184                 return -1;
185
186         if (!dm_task_run(task)) {
187                 pb_log("Error executing dm-task\n");
188                 return -1;
189         }
190
191         /* Wait for /dev/mapper/ entry to appear */
192         dm_udev_wait(cookie);
193
194         dm_task_destroy(task);
195         return 0;
196 }
197
198 static int create_base(struct discover_device *device)
199 {
200         struct target target;
201         char *name = NULL;
202         int rc = -1;
203
204         if (!device->ramdisk)
205                 return rc;
206
207         target.start_sector = 0;
208         target.end_sector = device->ramdisk->sectors;
209
210         target.ttype = talloc_asprintf(device,  "linear");
211         target.params = talloc_asprintf(device, "%s 0", device->device_path);
212         if (!target.ttype || !target.params) {
213                 pb_log("Failed to allocate map parameters\n");
214                 goto out;
215         }
216
217         name = talloc_asprintf(device, "%s-base", device->device->id);
218         if (!name || run_create_task(name, &target))
219                 goto out;
220
221         device->ramdisk->base = talloc_asprintf(device, "/dev/mapper/%s-base",
222                                         device->device->id);
223         if (!device->ramdisk->base) {
224                 pb_log("Failed to track new device /dev/mapper/%s-base\n",
225                         device->device->id);
226                 goto out;
227         }
228
229         rc = 0;
230
231 out:
232         talloc_free(name);
233         talloc_free(target.params);
234         talloc_free(target.ttype);
235         return rc;
236 }
237
238 static int create_origin(struct discover_device *device)
239 {
240         struct target target;
241         char *name = NULL;
242         int rc = -1;
243
244         if (!device->ramdisk || !device->ramdisk->base)
245                 return -1;
246
247         target.start_sector = 0;
248         target.end_sector = device->ramdisk->sectors;
249
250         target.ttype = talloc_asprintf(device,  "snapshot-origin");
251         target.params = talloc_asprintf(device, "%s", device->ramdisk->base);
252         if (!target.ttype || !target.params) {
253                 pb_log("Failed to allocate map parameters\n");
254                 goto out;
255         }
256
257         name = talloc_asprintf(device, "%s-origin", device->device->id);
258         if (!name || run_create_task(name, &target))
259                 goto out;
260
261         device->ramdisk->origin = talloc_asprintf(device,
262                                         "/dev/mapper/%s-origin",
263                                         device->device->id);
264         if (!device->ramdisk->origin) {
265                 pb_log("Failed to track new device /dev/mapper/%s-origin\n",
266                        device->device->id);
267                 goto out;
268         }
269
270         rc = 0;
271
272 out:
273         talloc_free(name);
274         talloc_free(target.params);
275         talloc_free(target.ttype);
276         return rc;
277 }
278
279 static int create_snapshot(struct discover_device *device)
280 {
281         struct target target;
282         int rc = -1;
283
284         if (!device->ramdisk || !device->ramdisk->base ||
285             !device->ramdisk->origin)
286                 return -1;
287
288         target.start_sector = 0;
289         target.end_sector = device->ramdisk->sectors;
290
291         target.ttype = talloc_asprintf(device,  "snapshot");
292         target.params = talloc_asprintf(device, "%s %s P 8",
293                  device->ramdisk->base, device->ramdisk->path);
294         if (!target.ttype || !target.params) {
295                 pb_log("Failed to allocate snapshot parameters\n");
296                 goto out;
297         }
298
299         if (run_create_task(device->device->id, &target))
300                 goto out;
301
302         device->ramdisk->snapshot = talloc_asprintf(device, "/dev/mapper/%s",
303                                                 device->device->id);
304         if (!device->ramdisk->snapshot) {
305                 pb_log("Failed to track new device /dev/mapper/%s\n",
306                        device->device->id);
307                 goto out;
308         }
309
310         rc = 0;
311
312 out:
313         talloc_free(target.params);
314         talloc_free(target.ttype);
315         return rc;
316 }
317
318 int devmapper_init_snapshot(struct device_handler *handler,
319                      struct discover_device *device)
320 {
321         struct ramdisk_device *ramdisk;
322
323         ramdisk = device_handler_get_ramdisk(handler);
324         if (!ramdisk) {
325                 pb_log("No ramdisk available for snapshot %s\n",
326                        device->device->id);
327                 return -1;
328         }
329
330         ramdisk->sectors = get_block_sectors(device);
331         if (!ramdisk->sectors) {
332                 pb_log("Error retreiving sectors for %s\n",
333                        device->device->id);
334                 return -1;
335         }
336
337         device->ramdisk = ramdisk;
338
339         /* Create linear map */
340         if (create_base(device)) {
341                 pb_log("Error creating linear base\n");
342                 goto err;
343         }
344
345         /* Create snapshot-origin */
346         if (create_origin(device)) {
347                 pb_log("Error creating snapshot-origin\n");
348                 goto err;
349         }
350
351         if (set_device_active(device->ramdisk->origin, false)) {
352                 pb_log("Failed to suspend origin\n");
353                 goto err;
354         }
355
356         /* Create snapshot */
357         if (create_snapshot(device)) {
358                 pb_log("Error creating snapshot\n");
359                 goto err;
360         }
361
362         if (set_device_active(device->ramdisk->origin, true)) {
363                 pb_log("Failed to resume origin\n");
364                 goto err;
365         }
366
367         pb_log("Snapshot successfully created for %s\n", device->device->id);
368
369         return 0;
370
371 err:
372         pb_log("Error creating snapshot devices for %s\n", device->device->id);
373         devmapper_destroy_snapshot(device);
374         return -1;
375 }
376
377 /* Destroy specific dm device */
378 static int destroy_device(const char *dm_name)
379 {
380         struct dm_task *task;
381         uint32_t cookie;
382         int rc = -1;
383
384         task = dm_task_create(DM_DEVICE_REMOVE);
385         if (!task) {
386                 pb_log("%s: could not create dm_task\n", __func__);
387                 return -1;
388         }
389
390         if (!dm_task_set_name(task, dm_name)) {
391                 pb_log("No dm device named '%s'\n", dm_name);
392                 goto out;
393         }
394
395         if (!set_cookie(task, &cookie))
396                 goto out;
397
398         if (!dm_task_run(task)) {
399                 pb_log("Unable to remove device '%s'\n", dm_name);
400                 goto out;
401         }
402
403         rc = 0;
404
405         /* Wait for /dev/mapper/ entries to be removed */
406         dm_udev_wait(cookie);
407
408 out:
409         dm_task_destroy(task);
410         return rc;
411 }
412
413 /* Destroy all dm devices related to a discover_device's snapshot */
414 int devmapper_destroy_snapshot(struct discover_device *device)
415 {
416         int rc = -1;
417
418         if (!device->ramdisk)
419                 return 0;
420
421         if (device->mounted) {
422                 pb_log("Can not remove snapshot: %s is mounted\n",
423                        device->device->id);
424                 return -1;
425         }
426
427         /* Clean up dm devices in order */
428         if (device->ramdisk->snapshot)
429                 if (destroy_device(device->ramdisk->snapshot))
430                         goto out;
431
432         if (device->ramdisk->origin)
433                 if (destroy_device(device->ramdisk->origin))
434                         goto out;
435
436         if (device->ramdisk->base)
437                 if (destroy_device(device->ramdisk->base))
438                         goto out;
439
440         rc = 0;
441 out:
442         if (rc)
443                 pb_log("Warning: %s snapshot not cleanly removed\n",
444                        device->device->id);
445         device_handler_release_ramdisk(device);
446         return rc;
447 }
448
449 static int reload_snapshot(struct discover_device *device, bool merge)
450 {
451         struct target target;
452         struct dm_task *task;
453         int rc = -1;
454
455         target.start_sector = 0;
456         target.end_sector = device->ramdisk->sectors;
457
458         if (merge) {
459                 target.ttype = talloc_asprintf(device,  "snapshot-merge");
460                 target.params = talloc_asprintf(device, "%s %s P 8",
461                          device->ramdisk->base, device->ramdisk->path);
462         } else {
463                 target.ttype = talloc_asprintf(device,  "snapshot-origin");
464                 target.params = talloc_asprintf(device, "%s",
465                          device->ramdisk->base);
466         }
467         if (!target.ttype || !target.params) {
468                 pb_log("%s: failed to allocate parameters\n", __func__);
469                 goto err1;
470         }
471
472         task = dm_task_create(DM_DEVICE_RELOAD);
473         if (!task) {
474                 pb_log("%s: Error creating task\n", __func__);
475                 goto err1;
476         }
477
478         if (!dm_task_set_name(task, device->ramdisk->origin)) {
479                 pb_log("No dm-device named '%s'\n", device->ramdisk->origin);
480                 goto err2;
481         }
482
483         if (!dm_task_add_target(task, target.start_sector, target.end_sector,
484                                 target.ttype, target.params)) {
485                 pb_log("%s: Failed to set target\n", __func__);
486                 goto err2;
487         }
488
489         if (!dm_task_run(task)) {
490                 pb_log("Failed to reload %s\n", device->ramdisk->origin);
491                 goto err2;
492         }
493
494         rc = 0;
495 err2:
496         dm_task_destroy(task);
497 err1:
498         talloc_free(target.ttype);
499         talloc_free(target.params);
500         return rc;
501 }
502
503 int devmapper_merge_snapshot(struct discover_device *device)
504 {
505         if (device->mounted) {
506                 pb_log("%s: %s still mounted\n", __func__, device->device->id);
507                 return -1;
508         }
509
510         /* Suspend origin device */
511         if (set_device_active(device->ramdisk->origin, false)) {
512                 pb_log("%s: failed to suspend %s\n",
513                        __func__, device->ramdisk->origin);
514                 return -1;
515         }
516
517         /* Destroy snapshot */
518         if (destroy_device(device->ramdisk->snapshot)) {
519                 /* The state of the snapshot is unknown, but try to
520                  * resume to allow the snapshot to be remounted */
521                 set_device_active(device->ramdisk->origin, true);
522                 return -1;
523         }
524         talloc_free(device->ramdisk->snapshot);
525         device->ramdisk->snapshot = NULL;
526
527         /* Reload origin device for merging */
528         reload_snapshot(device, true);
529
530         /* Resume origin device */
531         set_device_active(device->ramdisk->origin, true);
532
533         /* Block until merge complete */
534         while (!snapshot_merge_complete(device->ramdisk->origin))
535                 usleep(MERGE_INTERVAL_US);
536
537         /* Suspend origin device */
538         set_device_active(device->ramdisk->origin, false);
539
540         /* Reload origin device */
541         reload_snapshot(device, false);
542
543         /* Re-create snapshot */
544         if (create_snapshot(device))
545                 return -1;
546
547         /* Resume origin device */
548         return set_device_active(device->ramdisk->origin, true);
549 }