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