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