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