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