Check device mapper communication and warn user in case the communication fails....
[platform/upstream/cryptsetup.git] / lib / libdevmapper.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdarg.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/sysmacros.h>
9 #include <unistd.h>
10 #include <dirent.h>
11 #include <errno.h>
12 #include <libdevmapper.h>
13
14 #include "libcryptsetup.h"
15 #include "internal.h"
16
17 #define DEVICE_DIR      "/dev"
18
19 #define CRYPT_TARGET    "crypt"
20
21 #define UDEVSETTLE      "/sbin/udevsettle"
22
23 static void run_udevsettle(void)
24 {
25         system(UDEVSETTLE);
26 }
27
28 static void set_dm_error(int level, const char *file, int line,
29                          const char *f, ...)
30 {
31         va_list va;
32
33         if (level > 3)
34                 return;
35
36         va_start(va, f);
37         set_error_va(f, va);
38         va_end(va);
39 }
40
41 static int _dm_simple(int task, const char *name);
42
43 static int dm_init(void)
44 {
45         dm_log_init(set_dm_error);
46         if (!_dm_simple(DM_DEVICE_LIST_VERSIONS, "test")) {
47                 set_error("Cannot communicate with device-mapper. Is the dm_mod module loaded?");
48                 return -1;
49         }
50
51         return 1;       /* unsafe memory */
52 }
53
54 static void dm_exit(void)
55 {
56         dm_log_init(NULL);
57         dm_lib_release();
58 }
59
60 static void flush_dm_workqueue(void)
61 {
62         /* 
63          * Unfortunately this is the only way to trigger libdevmapper's
64          * update_nodes function 
65          */ 
66         dm_exit(); 
67         dm_init();
68 }
69
70 static char *__lookup_dev(char *path, dev_t dev)
71 {
72         struct dirent *entry;
73         struct stat st;
74         char *ptr;
75         char *result = NULL;
76         DIR *dir;
77         int space;
78
79         path[PATH_MAX - 1] = '\0';
80         ptr = path + strlen(path);
81         *ptr++ = '/';
82         *ptr = '\0';
83         space = PATH_MAX - (ptr - path);
84
85         dir = opendir(path);
86         if (!dir)
87                 return NULL;
88
89         while((entry = readdir(dir))) {
90                 if (entry->d_name[0] == '.' &&
91                     (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' &&
92                                                   entry->d_name[2] == '\0')))
93                         continue;
94
95                 strncpy(ptr, entry->d_name, space);
96                 if (lstat(path, &st) < 0)
97                         continue;
98
99                 if (S_ISDIR(st.st_mode)) {
100                         result = __lookup_dev(path, dev);
101                         if (result)
102                                 break;
103                 } else if (S_ISBLK(st.st_mode)) {
104                         if (st.st_rdev == dev) {
105                                 result = strdup(path);
106                                 break;
107                         }
108                 }
109         }
110
111         closedir(dir);
112
113         return result;
114 }
115
116 static char *lookup_dev(const char *dev)
117 {
118         uint32_t major, minor;
119         char buf[PATH_MAX + 1];
120
121         if (sscanf(dev, "%" PRIu32 ":%" PRIu32, &major, &minor) != 2)
122                 return NULL;
123
124         strncpy(buf, DEVICE_DIR, PATH_MAX);
125         buf[PATH_MAX] = '\0';
126
127         return __lookup_dev(buf, makedev(major, minor));
128 }
129
130 static char *get_params(struct crypt_options *options, const char *key)
131 {
132         char *params;
133         char *hexkey;
134         int i;
135
136         hexkey = safe_alloc(options->key_size * 2 + 1);
137         if (!hexkey) {
138                 set_error("Memory allocation problem");
139                 return NULL;
140         }
141
142         for(i = 0; i < options->key_size; i++)
143                 sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
144
145         params = safe_alloc(strlen(hexkey) + strlen(options->cipher) +
146                             strlen(options->device) + 64);
147         if (!params) {
148                 set_error("Memory allocation problem");
149                 goto out;
150         }
151
152         sprintf(params, "%s %s %" PRIu64 " %s %" PRIu64,
153                 options->cipher, hexkey, options->skip,
154                 options->device, options->offset);
155
156 out:
157         safe_free(hexkey);
158
159         return params;
160 }
161
162 static int dm_create_device(int reload, struct crypt_options *options,
163                             const char *key)
164 {
165         struct dm_task *dmt = NULL;
166         struct dm_task *dmt_query = NULL;
167         struct dm_info dmi;
168         char *params = NULL;
169         int r = -EINVAL;
170
171         params = get_params(options, key);
172         if (!params)
173                 goto out_no_removal;
174         if (!(dmt = dm_task_create(reload ? DM_DEVICE_RELOAD
175                                           : DM_DEVICE_CREATE)))
176                 goto out;
177         if (!dm_task_set_name(dmt, options->name))
178                 goto out;
179         if (options->flags & CRYPT_FLAG_READONLY && !dm_task_set_ro(dmt))
180                 goto out;
181         if (!dm_task_add_target(dmt, 0, options->size, CRYPT_TARGET, params))
182                 goto out;
183         if (!dm_task_run(dmt))
184                 goto out;
185
186         if (reload) {
187                 dm_task_destroy(dmt);
188                 if (!(dmt = dm_task_create(DM_DEVICE_RESUME)))
189                         goto out;
190                 if (!dm_task_set_name(dmt, options->name))
191                         goto out;
192                 if (!dm_task_run(dmt))
193                         goto out;
194         }
195
196         if (!dm_task_get_info(dmt, &dmi))
197                 goto out;
198         if (dmi.read_only)
199                 options->flags |= CRYPT_FLAG_READONLY;
200
201         /* run udevsettle to avoid a race in libdevmapper causing busy dm devices */
202         run_udevsettle();
203
204         r = 0;
205         
206 out:
207         if (r < 0 && !reload) {
208                 char *error = (char *)get_error();
209                 if (error)
210                         error = strdup(error);
211                 if (dmt)
212                         dm_task_destroy(dmt);
213
214                 if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
215                         goto out_restore_error;
216                 if (!dm_task_set_name(dmt, options->name))
217                         goto out_restore_error;
218                 if (!dm_task_run(dmt))
219                         goto out_restore_error;
220
221 out_restore_error:
222                 set_error("%s", error);
223                 if (error)
224                         free(error);
225         }
226
227 out_no_removal:
228         if (params)
229                 safe_free(params);
230         if (dmt)
231                 dm_task_destroy(dmt);
232         if(dmt_query)
233                 dm_task_destroy(dmt_query);
234         flush_dm_workqueue();
235         return r;
236 }
237
238 static int dm_query_device(int details, struct crypt_options *options,
239                            char **key)
240 {
241         struct dm_task *dmt;
242         struct dm_info dmi;
243         uint64_t start, length;
244         char *target_type, *params;
245         void *next = NULL;
246         int r = -EINVAL;
247
248         if (!(dmt = dm_task_create(details ? DM_DEVICE_TABLE
249                                            : DM_DEVICE_STATUS)))
250                 goto out;
251         if (!dm_task_set_name(dmt, options->name))
252                 goto out;
253         r = -ENODEV;
254         if (!dm_task_run(dmt))
255                 goto out;
256
257         r = -EINVAL;
258         if (!dm_task_get_info(dmt, &dmi))
259                 goto out;
260
261         if (!dmi.exists) {
262                 r = -ENODEV;
263                 goto out;
264         }
265
266         next = dm_get_next_target(dmt, next, &start, &length,
267                                   &target_type, &params);
268         if (!target_type || strcmp(target_type, CRYPT_TARGET) != 0 ||
269             start != 0 || next)
270                 goto out;
271
272         options->hash = NULL;
273         options->cipher = NULL;
274         options->offset = 0;
275         options->skip = 0;
276         options->size = length;
277         if (details) {
278                 char *cipher, *key_, *device;
279                 uint64_t val64;
280
281                 set_error("Invalid dm table");
282
283                 cipher = strsep(&params, " ");
284                 key_ = strsep(&params, " ");
285                 if (!params)
286                         goto out;
287
288                 val64 = strtoull(params, &params, 10);
289                 if (*params != ' ')
290                         goto out;
291                 params++;
292                 options->skip = val64;
293
294                 device = strsep(&params, " ");
295                 if (!params)
296                         goto out;
297
298                 val64 = strtoull(params, &params, 10);
299                 if (*params)
300                         goto out;
301                 options->offset = val64;
302
303                 options->cipher = strdup(cipher);
304                 options->key_size = strlen(key_) / 2;
305                 if (key) {
306                         char buffer[3];
307                         char *endp;
308                         int i;
309
310                         *key = safe_alloc(options->key_size);
311                         if (!*key) {
312                                 set_error("Out of memory");
313                                 r = -ENOMEM;
314                                 goto out;
315                         }
316
317                         buffer[2] = '\0';
318                         for(i = 0; i < options->key_size; i++) {
319                                 memcpy(buffer, &key_[i * 2], 2);
320                                 (*key)[i] = strtoul(buffer, &endp, 16);
321                                 if (endp != &buffer[2]) {
322                                         safe_free(key);
323                                         *key = NULL;
324                                         goto out;
325                                 }
326                         }
327                 }
328                 memset(key_, 0, strlen(key_));
329                 options->device = lookup_dev(device);
330
331                 set_error(NULL);
332         }
333
334         r = (dmi.open_count > 0);
335
336 out:
337         if (dmt)
338                 dm_task_destroy(dmt);
339         if (r >= 0) {
340                 if (options->device)
341                         options->flags |= CRYPT_FLAG_FREE_DEVICE;
342                 if (options->cipher)
343                         options->flags |= CRYPT_FLAG_FREE_CIPHER;
344                 options->flags &= ~CRYPT_FLAG_READONLY;
345                 if (dmi.read_only)
346                         options->flags |= CRYPT_FLAG_READONLY;
347         } else {
348                 if (options->device) {
349                         free((char *)options->device);
350                         options->device = NULL;
351                         options->flags &= ~CRYPT_FLAG_FREE_DEVICE;
352                 }
353                 if (options->cipher) {
354                         free((char *)options->cipher);
355                         options->cipher = NULL;
356                         options->flags &= ~CRYPT_FLAG_FREE_CIPHER;
357                 }
358         }
359         return r;
360 }
361
362 static int dm_remove_device(struct crypt_options *options)
363 {
364         struct dm_task *dmt;
365         int r = -EINVAL;
366
367         if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
368                 goto out;
369         if (!dm_task_set_name(dmt, options->name))
370                 goto out;
371         if (!dm_task_run(dmt))
372                 goto out;
373
374         r = 0;
375
376 out:    
377         if (dmt)
378                 dm_task_destroy(dmt);
379         flush_dm_workqueue();
380         return r;
381 }
382
383
384 static const char *dm_get_dir(void)
385 {
386         return dm_dir();
387 }
388
389 struct setup_backend setup_libdevmapper_backend = {
390         .name = "dm-crypt",
391         .init = dm_init,
392         .exit = dm_exit,
393         .create = dm_create_device,
394         .status = dm_query_device,
395         .remove = dm_remove_device,
396         .dir = dm_get_dir
397 };