Move dm backend initialisation to library calls.
[platform/upstream/cryptsetup.git] / lib / libdevmapper.c
1 #include <sys/ioctl.h>
2 #include <dirent.h>
3 #include <errno.h>
4 #include <libdevmapper.h>
5 #include <linux/dm-ioctl.h>
6 #include <fcntl.h>
7 #include <linux/fs.h>
8 #include <uuid/uuid.h>
9
10 #include "internal.h"
11 #include "luks.h"
12
13 #define DEVICE_DIR              "/dev"
14 #define DM_UUID_PREFIX          "CRYPT-"
15 #define DM_UUID_PREFIX_LEN      6
16 #define DM_CRYPT_TARGET         "crypt"
17 #define RETRY_COUNT             5
18
19 static int _dm_use_count = 0;
20 static struct crypt_device *_context = NULL;
21
22 static void set_dm_error(int level, const char *file, int line,
23                          const char *f, ...)
24 {
25         char *msg = NULL;
26         va_list va;
27
28         va_start(va, f);
29         if (vasprintf(&msg, f, va) > 0) {
30                 if (level < 4) {
31                         log_err(_context, msg);
32                         log_err(_context, "\n");
33                 } else
34                         log_dbg(msg);
35         }
36         free(msg);
37         va_end(va);
38 }
39
40 static int _dm_simple(int task, const char *name);
41
42 int dm_init(struct crypt_device *context, int check_kernel)
43 {
44         if (!_dm_use_count++) {
45                 log_dbg("Initialising device-mapper backend%s.",
46                         check_kernel ? "" : " (NO kernel check requested)");
47                 if (check_kernel && !_dm_simple(DM_DEVICE_LIST_VERSIONS, NULL)) {
48                         log_err(context, _("Cannot initialize device-mapper. Is dm_mod kernel module loaded?\n"));
49                         return -1;
50                 }
51                 dm_log_init(set_dm_error);
52                 dm_log_init_verbose(10);
53         }
54
55         // FIXME: global context is not safe
56         if (context)
57                 _context = context;
58
59         return 1;       /* unsafe memory */
60 }
61
62 void dm_exit(void)
63 {
64         if (_dm_use_count && (!--_dm_use_count)) {
65                 log_dbg("Releasing device-mapper backend.");
66                 dm_log_init_verbose(0);
67                 dm_log_init(NULL);
68                 dm_lib_release();
69                 _context = NULL;
70         }
71 }
72
73 static char *__lookup_dev(char *path, dev_t dev)
74 {
75         struct dirent *entry;
76         struct stat st;
77         char *ptr;
78         char *result = NULL;
79         DIR *dir;
80         int space;
81
82         path[PATH_MAX - 1] = '\0';
83         ptr = path + strlen(path);
84         *ptr++ = '/';
85         *ptr = '\0';
86         space = PATH_MAX - (ptr - path);
87
88         dir = opendir(path);
89         if (!dir)
90                 return NULL;
91
92         while((entry = readdir(dir))) {
93                 if (entry->d_name[0] == '.' &&
94                     (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' &&
95                                                   entry->d_name[2] == '\0')))
96                         continue;
97
98                 strncpy(ptr, entry->d_name, space);
99                 if (lstat(path, &st) < 0)
100                         continue;
101
102                 if (S_ISDIR(st.st_mode)) {
103                         result = __lookup_dev(path, dev);
104                         if (result)
105                                 break;
106                 } else if (S_ISBLK(st.st_mode)) {
107                         if (st.st_rdev == dev) {
108                                 result = strdup(path);
109                                 break;
110                         }
111                 }
112         }
113
114         closedir(dir);
115
116         return result;
117 }
118
119 static char *lookup_dev(const char *dev)
120 {
121         uint32_t major, minor;
122         char buf[PATH_MAX + 1];
123
124         if (sscanf(dev, "%" PRIu32 ":%" PRIu32, &major, &minor) != 2)
125                 return NULL;
126
127         strncpy(buf, DEVICE_DIR, PATH_MAX);
128         buf[PATH_MAX] = '\0';
129
130         return __lookup_dev(buf, makedev(major, minor));
131 }
132
133 static int _dev_read_ahead(const char *dev, uint32_t *read_ahead)
134 {
135         int fd, r = 0;
136         long read_ahead_long;
137
138         if ((fd = open(dev, O_RDONLY)) < 0)
139                 return 0;
140
141         r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1;
142         close(fd);
143
144         if (r)
145                 *read_ahead = (uint32_t) read_ahead_long;
146
147         return r;
148 }
149
150 static void hex_key(char *hexkey, size_t key_size, const char *key)
151 {
152         int i;
153
154         for(i = 0; i < key_size; i++)
155                 sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
156 }
157
158 static char *get_params(const char *device, uint64_t skip, uint64_t offset,
159                         const char *cipher, size_t key_size, const char *key)
160 {
161         char *params;
162         char *hexkey;
163
164         hexkey = safe_alloc(key_size * 2 + 1);
165         if (!hexkey)
166                 return NULL;
167
168         hex_key(hexkey, key_size, key);
169
170         params = safe_alloc(strlen(hexkey) + strlen(cipher) + strlen(device) + 64);
171         if (!params)
172                 goto out;
173
174         sprintf(params, "%s %s %" PRIu64 " %s %" PRIu64,
175                 cipher, hexkey, skip, device, offset);
176
177 out:
178         safe_free(hexkey);
179         return params;
180 }
181
182 /* DM helpers */
183 static int _dm_simple(int task, const char *name)
184 {
185         int r = 0;
186         struct dm_task *dmt;
187
188         if (!(dmt = dm_task_create(task)))
189                 return 0;
190
191         if (name && !dm_task_set_name(dmt, name))
192                 goto out;
193
194         r = dm_task_run(dmt);
195
196       out:
197         dm_task_destroy(dmt);
198         return r;
199 }
200
201 static int _error_device(const char *name, size_t size)
202 {
203         struct dm_task *dmt;
204         int r = 0;
205
206         if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
207                 return 0;
208
209         if (!dm_task_set_name(dmt, name))
210                 goto error;
211
212         if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
213                 goto error;
214
215         if (!dm_task_set_ro(dmt))
216                 goto error;
217
218         if (!dm_task_no_open_count(dmt))
219                 goto error;
220
221         if (!dm_task_run(dmt))
222                 goto error;
223
224         if (!_dm_simple(DM_DEVICE_RESUME, name)) {
225                 _dm_simple(DM_DEVICE_CLEAR, name);
226                 goto error;
227         }
228
229         r = 1;
230
231 error:
232         dm_task_destroy(dmt);
233         return r;
234 }
235
236 int dm_remove_device(const char *name, int force, uint64_t size)
237 {
238         int r = -EINVAL;
239         int retries = force ? RETRY_COUNT : 1;
240         int error_target = 0;
241
242         if (!name || (force && !size))
243                 return -EINVAL;
244
245         do {
246                 r = _dm_simple(DM_DEVICE_REMOVE, name) ? 0 : -EINVAL;
247                 if (--retries && r) {
248                         log_dbg("WARNING: other process locked internal device %s, %s.",
249                                 name, retries ? "retrying remove" : "giving up");
250                         sleep(1);
251                         if (force && !error_target) {
252                                 /* If force flag is set, replace device with error, read-only target.
253                                  * it should stop processes from reading it and also removed underlying
254                                  * device from mapping, so it is usable again.
255                                  * Force flag should be used only for temporary devices, which are
256                                  * intended to work inside cryptsetup only!
257                                  * Anyway, if some process try to read temporary cryptsetup device,
258                                  * it is bug - no other process should try touch it (e.g. udev).
259                                  */
260                                 _error_device(name, size);
261                                 error_target = 1;
262                         }
263                 }
264         } while (r == -EINVAL && retries);
265
266         dm_task_update_nodes();
267
268         return r;
269 }
270
271 #define UUID_LEN 37 /* 36 + \0, libuuid ... */
272 /*
273  * UUID has format: CRYPT-<devicetype>-[<uuid>-]<device name>
274  * CRYPT-PLAIN-name
275  * CRYPT-LUKS1-00000000000000000000000000000000-name
276  * CRYPT-TEMP-name
277  */
278 static void dm_prepare_uuid(const char *name, const char *type, const char *uuid, char *buf, size_t buflen)
279 {
280         char *ptr, uuid2[UUID_LEN] = {0};
281         uuid_t uu;
282         int i = 0;
283
284         /* Remove '-' chars */
285         if (uuid && !uuid_parse(uuid, uu)) {
286                 for (ptr = uuid2, i = 0; i < UUID_LEN; i++)
287                         if (uuid[i] != '-') {
288                                 *ptr = uuid[i];
289                                 ptr++;
290                         }
291         }
292
293         i = snprintf(buf, buflen, DM_UUID_PREFIX "%s%s%s%s%s",
294                 type ?: "", type ? "-" : "",
295                 uuid2[0] ? uuid2 : "", uuid2[0] ? "-" : "",
296                 name);
297
298         log_dbg("DM-UUID is %s", buf);
299         if (i >= buflen)
300                 log_err(NULL, _("DM-UUID for device %s was truncated.\n"), name);
301 }
302
303 int dm_create_device(const char *name,
304                      const char *device,
305                      const char *cipher,
306                      const char *type,
307                      const char *uuid,
308                      uint64_t size,
309                      uint64_t skip,
310                      uint64_t offset,
311                      size_t key_size,
312                      const char *key,
313                      int read_only,
314                      int reload)
315 {
316         struct dm_task *dmt = NULL;
317         struct dm_task *dmt_query = NULL;
318         struct dm_info dmi;
319         char *params = NULL;
320         char *error = NULL;
321         char dev_uuid[DM_UUID_LEN] = {0};
322         int r = -EINVAL;
323         uint32_t read_ahead = 0;
324
325         params = get_params(device, skip, offset, cipher, key_size, key);
326         if (!params)
327                 goto out_no_removal;
328
329         /* All devices must have DM_UUID, only resize on old device is exception */
330         if (reload) {
331                 if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
332                         goto out_no_removal;
333
334                 if (!dm_task_set_name(dmt, name))
335                         goto out_no_removal;
336         } else {
337                 dm_prepare_uuid(name, type, uuid, dev_uuid, sizeof(dev_uuid));
338
339                 if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
340                         goto out_no_removal;
341
342                 if (!dm_task_set_name(dmt, name))
343                         goto out_no_removal;
344
345                 if (!dm_task_set_uuid(dmt, dev_uuid))
346                         goto out_no_removal;
347         }
348
349
350         if (read_only && !dm_task_set_ro(dmt))
351                 goto out_no_removal;
352         if (!dm_task_add_target(dmt, 0, size, DM_CRYPT_TARGET, params))
353                 goto out_no_removal;
354
355 #ifdef DM_READ_AHEAD_MINIMUM_FLAG
356         if (_dev_read_ahead(device, &read_ahead) &&
357             !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG))
358                 goto out_no_removal;
359 #endif
360
361         if (!dm_task_run(dmt))
362                 goto out_no_removal;
363
364         if (reload) {
365                 dm_task_destroy(dmt);
366                 if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
367                         goto out;
368                 if (!dm_task_set_name(dmt, name))
369                         goto out;
370                 if (uuid && !dm_task_set_uuid(dmt, dev_uuid))
371                         goto out;
372                 if (!dm_task_run(dmt))
373                         goto out;
374         }
375
376         if (!dm_task_get_info(dmt, &dmi))
377                 goto out;
378
379         r = 0;
380 out:
381         if (r < 0 && !reload) {
382                 if (get_error())
383                         error = strdup(get_error());
384
385                 dm_remove_device(name, 0, 0);
386
387                 if (error) {
388                         set_error(error);
389                         free(error);
390                 }
391         }
392
393 out_no_removal:
394         if (params)
395                 safe_free(params);
396         if (dmt)
397                 dm_task_destroy(dmt);
398         if(dmt_query)
399                 dm_task_destroy(dmt_query);
400         dm_task_update_nodes();
401         return r;
402 }
403
404 int dm_status_device(const char *name)
405 {
406         struct dm_task *dmt;
407         struct dm_info dmi;
408         uint64_t start, length;
409         char *target_type, *params;
410         void *next = NULL;
411         int r = -EINVAL;
412
413         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
414                 return -EINVAL;
415
416         if (!dm_task_set_name(dmt, name)) {
417                 r = -EINVAL;
418                 goto out;
419         }
420
421         if (!dm_task_run(dmt)) {
422                 r = -ENODEV;
423                 goto out;
424         }
425
426         if (!dm_task_get_info(dmt, &dmi)) {
427                 r = -EINVAL;
428                 goto out;
429         }
430
431         if (!dmi.exists) {
432                 r = -ENODEV;
433                 goto out;
434         }
435
436         next = dm_get_next_target(dmt, next, &start, &length,
437                                   &target_type, &params);
438         if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
439             start != 0 || next)
440                 r = -EINVAL;
441         else
442                 r = (dmi.open_count > 0);
443 out:
444         if (dmt)
445                 dm_task_destroy(dmt);
446
447         return r;
448 }
449
450 int dm_query_device(const char *name,
451                     char **device,
452                     uint64_t *size,
453                     uint64_t *skip,
454                     uint64_t *offset,
455                     char **cipher,
456                     int *key_size,
457                     char **key,
458                     int *read_only,
459                     int *suspended,
460                     char **uuid)
461 {
462         struct dm_task *dmt;
463         struct dm_info dmi;
464         uint64_t start, length, val64;
465         char *target_type, *params, *rcipher, *key_, *rdevice, *endp, buffer[3], *tmp_uuid;
466         void *next = NULL;
467         int i, r = -EINVAL;
468
469         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
470                 goto out;
471         if (!dm_task_set_name(dmt, name))
472                 goto out;
473         r = -ENODEV;
474         if (!dm_task_run(dmt))
475                 goto out;
476
477         r = -EINVAL;
478         if (!dm_task_get_info(dmt, &dmi))
479                 goto out;
480
481         if (!dmi.exists) {
482                 r = -ENODEV;
483                 goto out;
484         }
485
486         next = dm_get_next_target(dmt, next, &start, &length,
487                                   &target_type, &params);
488         if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
489             start != 0 || next)
490                 goto out;
491
492         if (size)
493                 *size = length;
494
495         rcipher = strsep(&params, " ");
496         /* cipher */
497         if (cipher)
498                 *cipher = strdup(rcipher);
499
500         /* skip */
501         key_ = strsep(&params, " ");
502         if (!params)
503                 goto out;
504         val64 = strtoull(params, &params, 10);
505         if (*params != ' ')
506                 goto out;
507         params++;
508         if (skip)
509                 *skip = val64;
510
511         /* device */
512         rdevice = strsep(&params, " ");
513         if (device)
514                 *device = lookup_dev(rdevice);
515
516         /*offset */
517         if (!params)
518                 goto out;
519         val64 = strtoull(params, &params, 10);
520         if (*params)
521                 goto out;
522         if (offset)
523                 *offset = val64;
524
525         /* key_size */
526         if (key_size)
527                 *key_size = strlen(key_) / 2;
528
529         /* key */
530         if (key_size && key) {
531                 *key = safe_alloc(*key_size);
532                 if (!*key) {
533                         r = -ENOMEM;
534                         goto out;
535                 }
536
537                 buffer[2] = '\0';
538                 for(i = 0; i < *key_size; i++) {
539                         memcpy(buffer, &key_[i * 2], 2);
540                         (*key)[i] = strtoul(buffer, &endp, 16);
541                         if (endp != &buffer[2]) {
542                                 safe_free(key);
543                                 *key = NULL;
544                                 goto out;
545                         }
546                 }
547         }
548         memset(key_, 0, strlen(key_));
549
550         if (read_only)
551                 *read_only = dmi.read_only;
552
553         if (suspended)
554                 *suspended = dmi.suspended;
555
556         if (uuid && (tmp_uuid = (char*)dm_task_get_uuid(dmt)) &&
557             !strncmp(tmp_uuid, DM_UUID_PREFIX, DM_UUID_PREFIX_LEN))
558                 *uuid = strdup(tmp_uuid + DM_UUID_PREFIX_LEN);
559
560         r = (dmi.open_count > 0);
561 out:
562         if (dmt)
563                 dm_task_destroy(dmt);
564
565         return r;
566 }
567
568 static int _dm_message(const char *name, const char *msg)
569 {
570         int r = 0;
571         struct dm_task *dmt;
572
573         if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
574                 return 0;
575
576         if (name && !dm_task_set_name(dmt, name))
577                 goto out;
578
579         if (!dm_task_set_sector(dmt, (uint64_t) 0))
580                 goto out;
581
582         if (!dm_task_set_message(dmt, msg))
583                 goto out;
584
585         r = dm_task_run(dmt);
586
587       out:
588         dm_task_destroy(dmt);
589         return r;
590 }
591
592 int dm_suspend_and_wipe_key(const char *name)
593 {
594         if (!_dm_simple(DM_DEVICE_SUSPEND, name))
595                 return -EINVAL;
596
597         if (!_dm_message(name, "key wipe")) {
598                 _dm_simple(DM_DEVICE_RESUME, name);
599                 return -EINVAL;
600         }
601
602         return 0;
603 }
604
605 int dm_resume_and_reinstate_key(const char *name,
606                                 size_t key_size,
607                                 const char *key)
608 {
609         int msg_size = key_size * 2 + 10; // key set <key>
610         char *msg;
611         int r = 0;
612
613         msg = safe_alloc(msg_size);
614         if (!msg)
615                 return -ENOMEM;
616
617         memset(msg, 0, msg_size);
618         strcpy(msg, "key set ");
619         hex_key(&msg[8], key_size, key);
620
621         if (!_dm_message(name, msg) ||
622             !_dm_simple(DM_DEVICE_RESUME, name))
623                 r = -EINVAL;
624
625         safe_free(msg);
626         return r;
627 }
628
629 const char *dm_get_dir(void)
630 {
631         return dm_dir();
632 }