Require device device-mapper to build and do not use backend wrapper for dm 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 <fcntl.h>
6 #include <linux/fs.h>
7
8 #include "internal.h"
9 #include "luks.h"
10
11 #define DEVICE_DIR              "/dev"
12 #define DM_UUID_PREFIX          "CRYPT-"
13 #define DM_UUID_PREFIX_LEN      6
14 #define DM_UUID_LEN             UUID_STRING_L
15 #define DM_CRYPT_TARGET         "crypt"
16 #define RETRY_COUNT             5
17
18 static int _dm_use_count = 0;
19 static struct crypt_device *_context = NULL;
20
21 static void set_dm_error(int level, const char *file, int line,
22                          const char *f, ...)
23 {
24         va_list va;
25
26         if (level > 3)
27                 return;
28
29         va_start(va, f);
30         set_error_va(f, va);
31         va_end(va);
32 }
33
34 static int _dm_simple(int task, const char *name);
35
36 int dm_init(struct crypt_device *context, int check_kernel)
37 {
38         if (!_dm_use_count++) {
39                 if (check_kernel && !_dm_simple(DM_DEVICE_LIST_VERSIONS, NULL))
40                         return -1;
41                 dm_log_init(set_dm_error);
42                 dm_log_init_verbose(10);
43         }
44
45         if (context)
46                 _context = context;
47
48         return 1;       /* unsafe memory */
49 }
50
51 void dm_exit(void)
52 {
53         if (_dm_use_count && (!--_dm_use_count)) {
54                 dm_log_init_verbose(0);
55                 dm_log_init(NULL);
56                 dm_lib_release();
57                 _context = NULL;
58         }
59 }
60
61 static char *__lookup_dev(char *path, dev_t dev)
62 {
63         struct dirent *entry;
64         struct stat st;
65         char *ptr;
66         char *result = NULL;
67         DIR *dir;
68         int space;
69
70         path[PATH_MAX - 1] = '\0';
71         ptr = path + strlen(path);
72         *ptr++ = '/';
73         *ptr = '\0';
74         space = PATH_MAX - (ptr - path);
75
76         dir = opendir(path);
77         if (!dir)
78                 return NULL;
79
80         while((entry = readdir(dir))) {
81                 if (entry->d_name[0] == '.' &&
82                     (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' &&
83                                                   entry->d_name[2] == '\0')))
84                         continue;
85
86                 strncpy(ptr, entry->d_name, space);
87                 if (lstat(path, &st) < 0)
88                         continue;
89
90                 if (S_ISDIR(st.st_mode)) {
91                         result = __lookup_dev(path, dev);
92                         if (result)
93                                 break;
94                 } else if (S_ISBLK(st.st_mode)) {
95                         if (st.st_rdev == dev) {
96                                 result = strdup(path);
97                                 break;
98                         }
99                 }
100         }
101
102         closedir(dir);
103
104         return result;
105 }
106
107 static char *lookup_dev(const char *dev)
108 {
109         uint32_t major, minor;
110         char buf[PATH_MAX + 1];
111
112         if (sscanf(dev, "%" PRIu32 ":%" PRIu32, &major, &minor) != 2)
113                 return NULL;
114
115         strncpy(buf, DEVICE_DIR, PATH_MAX);
116         buf[PATH_MAX] = '\0';
117
118         return __lookup_dev(buf, makedev(major, minor));
119 }
120
121 static int _dev_read_ahead(const char *dev, uint32_t *read_ahead)
122 {
123         int fd, r = 0;
124         long read_ahead_long;
125
126         if ((fd = open(dev, O_RDONLY)) < 0)
127                 return 0;
128
129         r = ioctl(fd, BLKRAGET, &read_ahead_long) ? 0 : 1;
130         close(fd);
131
132         if (r)
133                 *read_ahead = (uint32_t) read_ahead_long;
134
135         return r;
136 }
137
138 static char *get_params(const char *device, uint64_t skip, uint64_t offset,
139                         const char *cipher, size_t key_size, const char *key)
140 {
141         char *params;
142         char *hexkey;
143         int i;
144
145         hexkey = safe_alloc(key_size * 2 + 1);
146         if (!hexkey)
147                 return NULL;
148
149         for(i = 0; i < key_size; i++)
150                 sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
151
152         params = safe_alloc(strlen(hexkey) + strlen(cipher) + strlen(device) + 64);
153         if (!params)
154                 goto out;
155
156         sprintf(params, "%s %s %" PRIu64 " %s %" PRIu64,
157                 cipher, hexkey, skip, device, offset);
158
159 out:
160         safe_free(hexkey);
161         return params;
162 }
163
164 /* DM helpers */
165 static int _dm_simple(int task, const char *name)
166 {
167         int r = 0;
168         struct dm_task *dmt;
169
170         if (!(dmt = dm_task_create(task)))
171                 return 0;
172
173         if (name && !dm_task_set_name(dmt, name))
174                 goto out;
175
176         r = dm_task_run(dmt);
177
178       out:
179         dm_task_destroy(dmt);
180         return r;
181 }
182
183 static int _error_device(const char *name, size_t size)
184 {
185         struct dm_task *dmt;
186         int r = 0;
187
188         if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
189                 return 0;
190
191         if (!dm_task_set_name(dmt, name))
192                 goto error;
193
194         if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
195                 goto error;
196
197         if (!dm_task_set_ro(dmt))
198                 goto error;
199
200         if (!dm_task_no_open_count(dmt))
201                 goto error;
202
203         if (!dm_task_run(dmt))
204                 goto error;
205
206         if (!_dm_simple(DM_DEVICE_RESUME, name)) {
207                 _dm_simple(DM_DEVICE_CLEAR, name);
208                 goto error;
209         }
210
211         r = 1;
212
213 error:
214         dm_task_destroy(dmt);
215         return r;
216 }
217
218 int dm_remove_device(const char *name, int force, uint64_t size)
219 {
220         int r = -EINVAL;
221         int retries = force ? RETRY_COUNT : 1;
222
223         if (!name || (force && !size))
224                 return -EINVAL;
225
226         /* If force flag is set, replace device with error, read-only target.
227          * it should stop processes from reading it and also removed underlying
228          * device from mapping, so it is usable again.
229          * Force flag should be used only for temporary devices, which are
230          * intended to work inside cryptsetup only!
231          * Anyway, if some process try to read temporary cryptsetup device,
232          * it is bug - no other process should try touch it (e.g. udev).
233          */
234         if (force) {
235                  _error_device(name, size);
236                 retries = RETRY_COUNT;
237         }
238
239         do {
240                 r = _dm_simple(DM_DEVICE_REMOVE, name) ? 0 : -EINVAL;
241                 if (--retries && r) {
242                         sleep(1);
243                 }
244         } while (r == -EINVAL && retries);
245
246         dm_task_update_nodes();
247
248         return r;
249 }
250
251 int dm_create_device(const char *name,
252                      const char *device,
253                      const char *cipher,
254                      const char *uuid,
255                      uint64_t size,
256                      uint64_t skip,
257                      uint64_t offset,
258                      size_t key_size,
259                      const char *key,
260                      int read_only,
261                      int reload)
262 {
263         struct dm_task *dmt = NULL;
264         struct dm_task *dmt_query = NULL;
265         struct dm_info dmi;
266         char *params = NULL;
267         char *error = NULL;
268         char dev_uuid[DM_UUID_PREFIX_LEN + DM_UUID_LEN + 1] = {0};
269         int r = -EINVAL;
270         uint32_t read_ahead = 0;
271
272         params = get_params(device, skip, offset, cipher, key_size, key);
273         if (!params)
274                 goto out_no_removal;
275  
276         if (uuid) {
277                 strncpy(dev_uuid, DM_UUID_PREFIX, DM_UUID_PREFIX_LEN);
278                 strncpy(dev_uuid + DM_UUID_PREFIX_LEN, uuid, DM_UUID_LEN);
279                 dev_uuid[DM_UUID_PREFIX_LEN + DM_UUID_LEN] = '\0';
280         }
281
282         if (!(dmt = dm_task_create(reload ? DM_DEVICE_RELOAD
283                                           : DM_DEVICE_CREATE)))
284                 goto out_no_removal;
285         if (!dm_task_set_name(dmt, name))
286                 goto out_no_removal;
287         if (read_only && !dm_task_set_ro(dmt))
288                 goto out_no_removal;
289         if (!dm_task_add_target(dmt, 0, size, DM_CRYPT_TARGET, params))
290                 goto out_no_removal;
291
292 #ifdef DM_READ_AHEAD_MINIMUM_FLAG
293         if (_dev_read_ahead(device, &read_ahead) &&
294             !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG))
295                 goto out_no_removal;
296 #endif
297
298         if (uuid && !dm_task_set_uuid(dmt, dev_uuid))
299                 goto out_no_removal;
300
301         if (!dm_task_run(dmt))
302                 goto out_no_removal;
303
304         if (reload) {
305                 dm_task_destroy(dmt);
306                 if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
307                         goto out;
308                 if (!dm_task_set_name(dmt, name))
309                         goto out;
310                 if (uuid && !dm_task_set_uuid(dmt, dev_uuid))
311                         goto out;
312                 if (!dm_task_run(dmt))
313                         goto out;
314         }
315
316         if (!dm_task_get_info(dmt, &dmi))
317                 goto out;
318
319         r = 0;
320 out:
321         if (r < 0 && !reload) {
322                 if (get_error())
323                         error = strdup(get_error());
324
325                 dm_remove_device(name, 0, 0);
326
327                 if (error) {
328                         set_error(error);
329                         free(error);
330                 }
331         }
332
333 out_no_removal:
334         if (params)
335                 safe_free(params);
336         if (dmt)
337                 dm_task_destroy(dmt);
338         if(dmt_query)
339                 dm_task_destroy(dmt_query);
340         dm_task_update_nodes();
341         return r;
342 }
343
344 int dm_status_device(const char *name)
345 {
346         struct dm_task *dmt;
347         struct dm_info dmi;
348         uint64_t start, length;
349         char *target_type, *params;
350         void *next = NULL;
351         int r = -EINVAL;
352
353         if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
354                 return -EINVAL;
355
356         if (!dm_task_set_name(dmt, name)) {
357                 r = -EINVAL;
358                 goto out;
359         }
360
361         if (!dm_task_run(dmt)) {
362                 r = -ENODEV;
363                 goto out;
364         }
365
366         if (!dm_task_get_info(dmt, &dmi)) {
367                 r = -EINVAL;
368                 goto out;
369         }
370
371         if (!dmi.exists) {
372                 r = -ENODEV;
373                 goto out;
374         }
375
376         next = dm_get_next_target(dmt, next, &start, &length,
377                                   &target_type, &params);
378         if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
379             start != 0 || next)
380                 r = -EINVAL;
381         else
382                 r = (dmi.open_count > 0);
383 out:
384         if (dmt)
385                 dm_task_destroy(dmt);
386
387         return r;
388 }
389
390 int dm_query_device(const char *name,
391                     char **device,
392                     uint64_t *size,
393                     uint64_t *skip,
394                     uint64_t *offset,
395                     char **cipher,
396                     int *key_size,
397                     char **key,
398                     int *read_only)
399 {
400         struct dm_task *dmt;
401         struct dm_info dmi;
402         uint64_t start, length, val64;
403         char *target_type, *params, *rcipher, *key_, *rdevice, *endp, buffer[3];
404         void *next = NULL;
405         int i, r = -EINVAL;
406
407         if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
408                 goto out;
409         if (!dm_task_set_name(dmt, name))
410                 goto out;
411         r = -ENODEV;
412         if (!dm_task_run(dmt))
413                 goto out;
414
415         r = -EINVAL;
416         if (!dm_task_get_info(dmt, &dmi))
417                 goto out;
418
419         if (!dmi.exists) {
420                 r = -ENODEV;
421                 goto out;
422         }
423
424         next = dm_get_next_target(dmt, next, &start, &length,
425                                   &target_type, &params);
426         if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 ||
427             start != 0 || next)
428                 goto out;
429
430         if (size)
431                 *size = length;
432
433         rcipher = strsep(&params, " ");
434         /* cipher */
435         if (cipher)
436                 *cipher = strdup(rcipher);
437
438         /* skip */
439         key_ = strsep(&params, " ");
440         if (!params)
441                 goto out;
442         val64 = strtoull(params, &params, 10);
443         if (*params != ' ')
444                 goto out;
445         params++;
446         if (skip)
447                 *skip = val64;
448
449         /* device */
450         rdevice = strsep(&params, " ");
451         if (device)
452                 *device = lookup_dev(rdevice);
453
454         /*offset */
455         if (!params)
456                 goto out;
457         val64 = strtoull(params, &params, 10);
458         if (*params)
459                 goto out;
460         if (offset)
461                 *offset = val64;
462
463         /* key_size */
464         if (key_size)
465                 *key_size = strlen(key_) / 2;
466
467         /* key */
468         if (key_size && key) {
469                 *key = safe_alloc(*key_size);
470                 if (!*key) {
471                         r = -ENOMEM;
472                         goto out;
473                 }
474
475                 buffer[2] = '\0';
476                 for(i = 0; i < *key_size; i++) {
477                         memcpy(buffer, &key_[i * 2], 2);
478                         (*key)[i] = strtoul(buffer, &endp, 16);
479                         if (endp != &buffer[2]) {
480                                 safe_free(key);
481                                 *key = NULL;
482                                 goto out;
483                         }
484                 }
485         }
486         memset(key_, 0, strlen(key_));
487
488         /* read_only */
489         if (read_only)
490                 *read_only = dmi.read_only;
491
492         r = (dmi.open_count > 0);
493 out:
494         if (dmt)
495                 dm_task_destroy(dmt);
496
497         return r;
498 }
499
500 const char *dm_get_dir(void)
501 {
502         return dm_dir();
503 }