]> git.ozlabs.org Git - petitboot/blob - discover/devmapper.c
discover/platform-powerpc: Correct aux revision format
[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, "%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/%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, "%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/%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         int rc = -1;
354
355         if (!device->ramdisk || !device->ramdisk->base ||
356             !device->ramdisk->origin)
357                 return -1;
358
359         target.start_sector = 0;
360         target.end_sector = device->ramdisk->sectors;
361
362         target.ttype = talloc_asprintf(device,  "snapshot");
363         target.params = talloc_asprintf(device, "%s %s P 8",
364                  device->ramdisk->base, device->ramdisk->path);
365         if (!target.ttype || !target.params) {
366                 pb_log("Failed to allocate snapshot parameters\n");
367                 goto out;
368         }
369
370         if (run_create_task(device->device->id, &target))
371                 goto out;
372
373         device->ramdisk->snapshot = talloc_asprintf(device, "/dev/mapper/%s",
374                                                 device->device->id);
375         if (!device->ramdisk->snapshot) {
376                 pb_log("Failed to track new device /dev/mapper/%s\n",
377                        device->device->id);
378                 goto out;
379         }
380
381         rc = 0;
382
383 out:
384         talloc_free(target.params);
385         talloc_free(target.ttype);
386         return rc;
387 }
388
389 int devmapper_init_snapshot(struct device_handler *handler,
390                      struct discover_device *device)
391 {
392         struct ramdisk_device *ramdisk;
393
394         if (config_get()->disable_snapshots)
395                 return 0;
396
397         ramdisk = device_handler_get_ramdisk(handler);
398         if (!ramdisk) {
399                 pb_log("No ramdisk available for snapshot %s\n",
400                        device->device->id);
401                 return -1;
402         }
403
404         ramdisk->sectors = get_block_sectors(device);
405         if (!ramdisk->sectors) {
406                 pb_log("Error retreiving sectors for %s\n",
407                        device->device->id);
408                 return -1;
409         }
410
411         device->ramdisk = ramdisk;
412
413         /* Create linear map */
414         if (create_base(device)) {
415                 pb_log("Error creating linear base\n");
416                 goto err;
417         }
418
419         /* Create snapshot-origin */
420         if (create_origin(device)) {
421                 pb_log("Error creating snapshot-origin\n");
422                 goto err;
423         }
424
425         if (set_device_active(device->ramdisk->origin, false)) {
426                 pb_log("Failed to suspend origin\n");
427                 goto err;
428         }
429
430         /* Create snapshot */
431         if (create_snapshot(device)) {
432                 pb_log("Error creating snapshot\n");
433                 goto err;
434         }
435
436         if (set_device_active(device->ramdisk->origin, true)) {
437                 pb_log("Failed to resume origin\n");
438                 goto err;
439         }
440
441         pb_log("Snapshot successfully created for %s\n", device->device->id);
442
443         return 0;
444
445 err:
446         pb_log("Error creating snapshot devices for %s\n", device->device->id);
447         devmapper_destroy_snapshot(device);
448         return -1;
449 }
450
451 /* Destroy specific dm device */
452 static int destroy_device(const char *dm_name)
453 {
454         struct dm_task *task;
455         uint32_t cookie;
456         int rc = -1;
457
458         task = dm_task_create(DM_DEVICE_REMOVE);
459         if (!task) {
460                 pb_log("%s: could not create dm_task\n", __func__);
461                 return -1;
462         }
463
464         if (!dm_task_set_name(task, dm_name)) {
465                 pb_log("No dm device named '%s'\n", dm_name);
466                 goto out;
467         }
468
469         if (!set_cookie(task, &cookie))
470                 goto out;
471
472         if (!dm_task_run(task)) {
473                 pb_log("Unable to remove device '%s'\n", dm_name);
474                 goto out;
475         }
476
477         rc = 0;
478
479         /* Wait for /dev/mapper/ entries to be removed */
480         dm_udev_wait(cookie);
481
482 out:
483         dm_task_destroy(task);
484         return rc;
485 }
486
487 /* Destroy all dm devices related to a discover_device's snapshot */
488 int devmapper_destroy_snapshot(struct discover_device *device)
489 {
490         int rc = -1;
491
492         if (!device->ramdisk)
493                 return 0;
494
495         if (device->mounted) {
496                 pb_log("Can not remove snapshot: %s is mounted\n",
497                        device->device->id);
498                 return -1;
499         }
500
501         /* Clean up dm devices in order */
502         if (device->ramdisk->snapshot)
503                 if (destroy_device(device->ramdisk->snapshot))
504                         goto out;
505
506         if (device->ramdisk->origin)
507                 if (destroy_device(device->ramdisk->origin))
508                         goto out;
509
510         if (device->ramdisk->base)
511                 if (destroy_device(device->ramdisk->base))
512                         goto out;
513
514         rc = 0;
515 out:
516         if (rc)
517                 pb_log("Warning: %s snapshot not cleanly removed\n",
518                        device->device->id);
519         device_handler_release_ramdisk(device);
520         return rc;
521 }
522
523 static int reload_snapshot(struct discover_device *device, bool merge)
524 {
525         struct target target;
526         struct dm_task *task;
527         int rc = -1;
528
529         target.start_sector = 0;
530         target.end_sector = device->ramdisk->sectors;
531
532         if (merge) {
533                 target.ttype = talloc_asprintf(device,  "snapshot-merge");
534                 target.params = talloc_asprintf(device, "%s %s P 8",
535                          device->ramdisk->base, device->ramdisk->path);
536         } else {
537                 target.ttype = talloc_asprintf(device,  "snapshot-origin");
538                 target.params = talloc_asprintf(device, "%s",
539                          device->ramdisk->base);
540         }
541         if (!target.ttype || !target.params) {
542                 pb_log("%s: failed to allocate parameters\n", __func__);
543                 goto err1;
544         }
545
546         task = dm_task_create(DM_DEVICE_RELOAD);
547         if (!task) {
548                 pb_log("%s: Error creating task\n", __func__);
549                 goto err1;
550         }
551
552         if (!dm_task_set_name(task, device->ramdisk->origin)) {
553                 pb_log("No dm-device named '%s'\n", device->ramdisk->origin);
554                 goto err2;
555         }
556
557         if (!dm_task_add_target(task, target.start_sector, target.end_sector,
558                                 target.ttype, target.params)) {
559                 pb_log("%s: Failed to set target\n", __func__);
560                 goto err2;
561         }
562
563         if (!dm_task_run(task)) {
564                 pb_log("Failed to reload %s\n", device->ramdisk->origin);
565                 goto err2;
566         }
567
568         rc = 0;
569 err2:
570         dm_task_destroy(task);
571 err1:
572         talloc_free(target.ttype);
573         talloc_free(target.params);
574         return rc;
575 }
576
577 int devmapper_merge_snapshot(struct discover_device *device)
578 {
579         if (device->mounted) {
580                 pb_log("%s: %s still mounted\n", __func__, device->device->id);
581                 return -1;
582         }
583
584         /* Suspend origin device */
585         if (set_device_active(device->ramdisk->origin, false)) {
586                 pb_log("%s: failed to suspend %s\n",
587                        __func__, device->ramdisk->origin);
588                 return -1;
589         }
590
591         /* Destroy snapshot */
592         if (destroy_device(device->ramdisk->snapshot)) {
593                 /* The state of the snapshot is unknown, but try to
594                  * resume to allow the snapshot to be remounted */
595                 set_device_active(device->ramdisk->origin, true);
596                 return -1;
597         }
598         talloc_free(device->ramdisk->snapshot);
599         device->ramdisk->snapshot = NULL;
600
601         /* Reload origin device for merging */
602         reload_snapshot(device, true);
603
604         /* Resume origin device */
605         set_device_active(device->ramdisk->origin, true);
606
607         /* Block until merge complete */
608         while (!snapshot_merge_complete(device->ramdisk->origin))
609                 usleep(MERGE_INTERVAL_US);
610
611         /* Suspend origin device */
612         set_device_active(device->ramdisk->origin, false);
613
614         /* Reload origin device */
615         reload_snapshot(device, false);
616
617         /* Re-create snapshot */
618         if (create_snapshot(device))
619                 return -1;
620
621         /* Resume origin device */
622         return set_device_active(device->ramdisk->origin, true);
623 }