blob: ef44c90eab92c5055e346740d3b9174abc7e362a [file] [log] [blame]
The Android Open Source Project13f797d2009-02-10 15:44:07 -08001
2/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21#include <dirent.h>
22#include <unistd.h>
23#include <sched.h>
24#include <fcntl.h>
25
26#include <sys/mount.h>
27
28#include <linux/loop.h>
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -080029#include <linux/dm-ioctl.h>
The Android Open Source Project13f797d2009-02-10 15:44:07 -080030
31#include <cutils/config_utils.h>
32#include <cutils/properties.h>
33
34#include "vold.h"
35#include "devmapper.h"
36
37#define DEBUG_DEVMAPPER 1
38
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -080039static void *_align(void *ptr, unsigned int a)
40{
41 register unsigned long agn = --a;
42
43 return (void *) (((unsigned long) ptr + agn) & ~agn);
44}
45
46static struct dm_ioctl *_dm_ioctl_setup(struct devmapping *dm, int flags)
47{
48 void *buffer;
49 void *p;
50 const size_t min_size = 16 * 1024;
51 size_t len = sizeof(struct dm_ioctl);
52 struct dm_ioctl *io;
53 struct dm_target_spec *tgt;
54 int i;
55 char params[1024];
56 char key[80];
57
58 key[0] = '\0';
59 for (i = 0; i < (int) sizeof(dm->key); i++) {
60 char tmp[8];
61
62 sprintf(tmp, "%02x", dm->key[i]);
63 strcat(key, tmp);
64 }
65
66 char srcdev[128];
67
68 // XXX: Handle non crypt targets and non twofish (use param)
69 if (dm->src_type == dmsrc_loopback)
70 strcpy(srcdev, dm->type_data.loop.loop_dev);
71 else if (dm->src_type == dmsrc_partition)
72 strcpy(srcdev, dm->type_data.part.part_dev);
73
74 sprintf(params, "twofish %s 0 %s 0", key, srcdev);
75
76LOG_VOL("Params = '%s'", params);
77
78 if (len < min_size)
79 len = min_size;
80
81 if (!(buffer = malloc(len))) {
82 LOGE("out of memory");
83 return NULL;
84 }
85
86 memset(buffer, 0, len);
87 io = buffer;
88 tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
89
90 io->version[0] = 4;
91 io->version[1] = 0;
92 io->version[2] = 0;
93
94 io->data_size = len;
95 io->data_start = sizeof(struct dm_ioctl);
96
97 io->flags = flags;
98 io->dev = 0;
99
100 io->target_count = 1;
101 io->event_nr = 1;
102 strncpy(io->name, dm->target, sizeof(io->name));
103
104 tgt->status = 0;
105 tgt->sector_start = 0;
106 tgt->length = (dm->size_mb * (1024 * 1024)) / 512;
107 strncpy(tgt->target_type, "crypt", sizeof(tgt->target_type));
108
109 p = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
110 strcpy((char *) p, params);
111 p+= strlen(params) + 1;
112
113 p = _align(p, 8);
114 tgt->next = p - buffer;
115
116 return io;
117}
118
119static int get_next_available_dm()
120{
121 int i;
122
123 for (i = 0; i < 8; i++) {
124 char path[255];
125 sprintf(path, "/dev/block/dm-%d", i);
126 if ((access(path, F_OK) < 0) && (errno == ENOENT))
127 return i;
128 }
129
130 LOGE("Out of device mapper numbers");
131 return -1;
132}
133
134static int create_devmapping(struct devmapping *dm)
135{
136 struct dm_ioctl *io;
137 int rc, fd;
138
139#if DEBUG_DEVMAPPER
140 LOG_VOL("create_devmapping():");
141#endif
142
143 if (dm->dm_no < 0) {
144 LOGE("Invalid dm_no set");
145 return -EINVAL;
146 }
147
148 if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
149 LOGE("Error opening device mapper (%d)", errno);
150 return -errno;
151 }
152
153 if (!(io = _dm_ioctl_setup(dm, 0))) {
154 LOGE("Unable to setup ioctl (out of memory)");
155 close(fd);
156 return -ENOMEM;
157 }
158
159 if ((rc = ioctl(fd, DM_DEV_CREATE, io)) < 0) {
160 LOGE("device-mapper create ioctl failed (%d)", errno);
161 rc = -errno;
162 goto out_free;
163 }
164
165 free(io);
166
167 if (!(io = _dm_ioctl_setup(dm, DM_STATUS_TABLE_FLAG))) {
168 LOGE("Unable to setup ioctl (out of memory)");
169 rc = -ENOMEM;
170 goto out_nofree;
171 }
172
173 if ((rc = ioctl(fd, DM_TABLE_LOAD, io)) < 0) {
174 LOGE("device-mapper load ioctl failed (%d)", errno);
175 rc = -errno;
176 goto out_free;
177 }
178
179 free(io);
180
181 if (!(io = _dm_ioctl_setup(dm, 0))) {
182 LOGE("Unable to setup ioctl (out of memory)");
183 rc = -ENOMEM;
184 goto out_nofree;
185 }
186
187 if ((rc = ioctl(fd, DM_DEV_SUSPEND, io)) < 0) {
188 LOGE("device-mapper resume ioctl failed (%d)", errno);
189 rc = -errno;
190 goto out_free;
191 }
192
193out_free:
194 free (io);
195out_nofree:
196 close (fd);
197 return rc;
198}
199
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800200static int loopback_start(struct devmapping *dm)
201{
202 int i;
203 int fd;
204 char filename[255];
205 int rc;
206
207#if DEBUG_DEVMAPPER
208 LOG_VOL("loopback_start(%s):", dm->type_data.loop.loop_src);
209#endif
210
211 for (i = 0; i < MAX_LOOP; i++) {
212 struct loop_info info;
213
214 sprintf(filename, "/dev/block/loop%d", i);
215
216 if ((fd = open(filename, O_RDWR)) < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800217 LOGE("Unable to open %s (%s)", filename, strerror(errno));
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800218 return -errno;
219 }
220
221 rc = ioctl(fd, LOOP_GET_STATUS, &info);
222 if (rc < 0 && errno == ENXIO)
223 break;
224
225 close(fd);
226
227 if (rc < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800228 LOGE("Unable to get loop status for %s (%s)", filename,
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800229 strerror(errno));
230 return -errno;
231 }
232 }
233
234 if (i == MAX_LOOP) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800235 LOGE("Out of loop devices");
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800236 return -ENOSPC;
237 }
238
239 int file_fd;
240
241 if ((file_fd = open(dm->type_data.loop.loop_src, O_RDWR)) < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800242 LOGE("Unable to open %s (%s)", dm->type_data.loop.loop_src,
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800243 strerror(errno));
244 return -errno;
245 }
246
247 if (ioctl(fd, LOOP_SET_FD, file_fd) < 0) {
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800248 LOGE("Error setting up loopback interface (%s)", strerror(errno));
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800249 return -errno;
250 }
251
252 dm->type_data.loop.loop_dev = strdup(filename);
253 dm->type_data.loop.loop_no = i;
254
255 close(fd);
256 close(file_fd);
257
258#if DEBUG_DEVMAPPER
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800259 LOG_VOL("Loop setup on %s for %s", dm->type_data.loop.loop_dev,
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800260 dm->type_data.loop.loop_src);
261#endif
262
263 return 0;
264}
265
266int devmapper_start(struct devmapping *dm)
267{
268 int rc;
269 char src_blkdev_path[255];
270
271#if DEBUG_DEVMAPPER
272 LOG_VOL("devmapper_start()");
273#endif
274
275 if (dm->src_type == dmsrc_loopback) {
276 if ((rc = loopback_start(dm)) < 0)
277 return rc;
278 } else if (dm->src_type == dmsrc_partition) {
279 LOGE("partition maps not yet supported");
280 return -ENOSYS;
281 } else {
282 LOGE("devmapper_start(): Unsupported source type '%d'", dm->src_type);
283 return -ENOENT;
284 }
285
286 /*
287 * Configure the device mapper
288 */
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800289 if ((rc = create_devmapping(dm)) < 0) {
290 LOGE("Failed to create devmapping (%d)", rc);
291 // XXX: if loopback then tear down
292 return rc;
293 }
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800294
295 return 0;
296}
297
298struct devmapping *devmapper_init(char *src, char *src_type, uint32_t size_mb,
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800299 char *target, char *params, char *tgt_fs, char *mediapath)
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800300{
301 struct devmapping *dm;
302
303 if (!(dm = malloc(sizeof(struct devmapping)))) {
304 LOGE("devmapper_init(): out of memory");
305 return NULL;
306 }
307
308 memset(dm, 0, sizeof(struct devmapping));
309
310 if (!strcmp(src_type, "loopback_file")) {
311 dm->src_type = dmsrc_loopback;
312 dm->type_data.loop.loop_src = strdup(src);
313 } else if (!strncmp(src_type, "partition ", strlen("partition "))) {
314 dm->src_type = dmsrc_partition;
315 char *p = strtok(src_type, " ");
316 if (!p) {
317 LOGE("Invalid partition specifier");
318 goto out_free;
319 }
320 dm->type_data.part.part_type = strtoul(p, NULL, 0);
321 } else {
322 LOGE("Invalid src_type defined (%s)", src_type);
323 goto out_free;
324 }
325
326 // XXX: Validate these
327 dm->size_mb = size_mb;
328 dm->target = strdup(target);
329 dm->params = strdup(params);
330 dm->tgt_fs = strdup(tgt_fs);
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800331
332 if ((dm->dm_no = get_next_available_dm()) < 0)
333 goto out_free;
334
335 sprintf(mediapath, "/devices/virtual/block/dm-%d", dm->dm_no);
336
337 if (!(dm->media = media_create(mediapath,
338 "unknown",
339 "unknown",
340 media_devmapper))) {
341 LOGE("Unable to create media");
342 goto out_free;
343 }
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800344
345 return dm;
346 out_free:
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800347 if (dm->target)
348 free(dm->target);
349 if (dm->params)
350 free(dm->params);
351 if (dm->tgt_fs)
352 free(dm->tgt_fs);
353
354 free(dm);
The Android Open Source Project13f797d2009-02-10 15:44:07 -0800355 return NULL;
356}
The Android Open Source Project1b8e5a62009-02-13 12:57:54 -0800357
358int devmapper_genesis(struct devmapping *dm)
359{
360
361 if (dm->src_type == dmsrc_loopback) {
362 int fd;
363
364 LOG_VOL("devmapper_genesis(): Working on %s",
365 dm->type_data.loop.loop_src);
366
367 unlink(dm->type_data.loop.loop_src);
368
369 LOG_VOL("devmapper_genesis(): Creating imagefile (%u MB)",
370 dm->size_mb);
371
372 if ((fd = creat(dm->type_data.loop.loop_src, 0600)) < 0) {
373 LOGE("Error creating imagefile (%s)", strerror(errno));
374 return -errno;
375 }
376
377 if (ftruncate(fd, (dm->size_mb * (1024 * 1024))) < 0) {
378 LOGE("Error truncating imagefile (%s)", strerror(errno));
379 close(fd);
380 return -errno;
381 }
382 close(fd);
383 } else if (dm->src_type == dmsrc_partition) {
384 LOGE("partition maps not yet supported");
385 return -ENOSYS;
386 }
387
388 return devmapper_start(dm);
389}
390
391static int destroy_devmapping(struct devmapping *dm)
392{
393 struct dm_ioctl *io;
394 int dmFd;
395 int rc = 0;
396
397 LOG_VOL("destroy_devmapping():");
398
399 if ((dmFd = open("/dev/device-mapper", O_RDWR)) < 0) {
400 LOGE("Error opening device mapper (%d)", errno);
401 return -errno;
402 }
403
404 if (!(io = _dm_ioctl_setup(dm, DM_PERSISTENT_DEV_FLAG))) {
405 LOGE("Unable to setup ioctl (out of memory)");
406 rc = -ENOMEM;
407 goto out_nofree;
408 }
409
410 if ((rc = ioctl(dmFd, DM_DEV_REMOVE, io)) < 0) {
411 LOGE("device-mapper remove ioctl failed (%d)", errno);
412 rc = -errno;
413 goto out_free;
414 }
415
416out_free:
417 free (io);
418out_nofree:
419 close (dmFd);
420 return rc;
421}
422
423static int loopback_stop(struct devmapping *dm)
424{
425 char devname[255];
426 int device_fd;
427 int rc = 0;
428
429 LOG_VOL("loopback_stop():");
430
431 device_fd = open(dm->type_data.loop.loop_dev, O_RDONLY);
432 if (device_fd < 0) {
433 LOG_ERROR("Failed to open loop (%d)", errno);
434 return -errno;
435 }
436
437 if (ioctl(device_fd, LOOP_CLR_FD, 0) < 0) {
438 LOG_ERROR("Failed to destroy loop (%d)", errno);
439 rc = -errno;
440 }
441
442 close(device_fd);
443 return rc;
444}
445
446int devmapper_stop(struct devmapping *dm)
447{
448 int rc;
449
450 LOG_VOL("devmapper_stop():");
451
452 if ((rc = destroy_devmapping(dm)))
453 return rc;
454
455 if (dm->src_type == dmsrc_loopback) {
456 if ((rc = loopback_stop(dm)))
457 return rc;
458 } else if (dm->src_type == dmsrc_partition) {
459 LOGE("partition maps not yet supported");
460 return -ENOSYS;
461 }
462 return 0;
463}