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