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