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