blob: 3c34a9c52d1dd31e3db7dbabd70b9a48b24a89b0 [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -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
25#include <sys/mount.h>
26
27#include <cutils/config_utils.h>
28#include <cutils/properties.h>
29
30#include "vold.h"
31#include "volmgr.h"
32#include "blkdev.h"
33#include "ums.h"
34#include "format.h"
35#include "devmapper.h"
36
37#include "volmgr_ext3.h"
38#include "volmgr_vfat.h"
39
40#define DEBUG_VOLMGR 0
41
42static volume_t *vol_root = NULL;
43static boolean safe_mode = true;
44
45static struct volmgr_fstable_entry fs_table[] = {
The Android Open Source Projectf614d642009-03-18 17:39:49 -070046// { "ext3", ext_identify, ext_check, ext_mount , true },
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080047 { "vfat", vfat_identify, vfat_check, vfat_mount , false },
48 { NULL, NULL, NULL, NULL , false}
49};
50
51struct _volume_state_event_map {
52 volume_state_t state;
53 char *event;
54 char *property_val;
55};
56
57static struct _volume_state_event_map volume_state_strings[] = {
58 { volstate_unknown, "volstate_unknown:", "unknown" },
59 { volstate_nomedia, VOLD_EVT_NOMEDIA, VOLD_ES_PVAL_NOMEDIA },
60 { volstate_unmounted, VOLD_EVT_UNMOUNTED, VOLD_ES_PVAL_UNMOUNTED },
61 { volstate_checking, VOLD_EVT_CHECKING, VOLD_ES_PVAL_CHECKING },
62 { volstate_mounted, VOLD_EVT_MOUNTED, VOLD_ES_PVAL_MOUNTED },
63 { volstate_mounted_ro, VOLD_EVT_MOUNTED_RO, VOLD_ES_PVAL_MOUNTED_RO },
64 { volstate_badremoval, VOLD_EVT_BADREMOVAL, VOLD_ES_PVAL_BADREMOVAL },
65 { volstate_damaged, VOLD_EVT_DAMAGED, VOLD_ES_PVAL_DAMAGED },
66 { volstate_nofs, VOLD_EVT_NOFS, VOLD_ES_PVAL_NOFS },
67 { volstate_ums, VOLD_EVT_UMS, VOLD_ES_PVAL_UMS },
68 { 0, NULL, NULL }
69};
70
71
72static int volmgr_readconfig(char *cfg_path);
73static int volmgr_config_volume(cnode *node);
74static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy);
75static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev);
76static int _volmgr_start(volume_t *vol, blkdev_t *dev);
77static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev);
78static void *volmgr_start_fs_thread(void *arg);
79static void volmgr_start_fs_thread_sighandler(int signo);
80static void volume_setstate(volume_t *vol, volume_state_t state);
81static char *conv_volstate_to_eventstr(volume_state_t state);
82static char *conv_volstate_to_propstr(volume_state_t state);
83static int volume_send_state(volume_t *vol);
84static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg);
85static int _volmgr_enable_ums(volume_t *);
86static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg), boolean emit_statechange);
87static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange);
88static void _cb_volume_stopped_for_eject(volume_t *v, void *arg);
89static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg);
90static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev);
91static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg);
92static void volmgr_reaper_thread_sighandler(int signo);
93static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path);
94static int volmgr_send_eject_request(volume_t *v);
95static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked);
96
97static boolean _mountpoint_mounted(char *mp)
98{
99 char device[256];
100 char mount_path[256];
101 char rest[256];
102 FILE *fp;
103 char line[1024];
104
105 if (!(fp = fopen("/proc/mounts", "r"))) {
106 LOGE("Error opening /proc/mounts (%s)", strerror(errno));
107 return false;
108 }
109
110 while(fgets(line, sizeof(line), fp)) {
111 line[strlen(line)-1] = '\0';
112 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
113 if (!strcmp(mount_path, mp)) {
114 fclose(fp);
115 return true;
116 }
117
118 }
119
120 fclose(fp);
121 return false;
122}
123
124/*
125 * Public functions
126 */
127
128int volmgr_set_volume_key(char *mount_point, unsigned char *key)
129{
130 volume_t *v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
131
132 if (!v)
133 return -ENOENT;
134
135 if (v->media_type != media_devmapper) {
136 LOGE("Cannot set key on a non devmapper volume");
137 pthread_mutex_unlock(&v->lock);
138 return -EINVAL;
139 }
140
141 memcpy(v->dm->key, key, sizeof(v->dm->key));
142 pthread_mutex_unlock(&v->lock);
143 return 0;
144}
145
146int volmgr_format_volume(char *mount_point)
147{
148 int rc;
149 volume_t *v;
150
151 LOG_VOL("volmgr_format_volume(%s):", mount_point);
152
153 v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
154
155 if (!v)
156 return -ENOENT;
157
158 if (v->state == volstate_mounted ||
159 v->state == volstate_mounted_ro ||
160 v->state == volstate_ums ||
161 v->state == volstate_checking) {
162 LOGE("Can't format '%s', currently in state %d", mount_point, v->state);
163 pthread_mutex_unlock(&v->lock);
164 return -EBUSY;
165 } else if (v->state == volstate_nomedia &&
166 v->media_type != media_devmapper) {
167 LOGE("Can't format '%s', (no media)", mount_point);
168 pthread_mutex_unlock(&v->lock);
169 return -ENOMEDIUM;
170 }
171
172 // XXX:Reject if the underlying source media is not present
173
174 if (v->media_type == media_devmapper) {
175 if ((rc = devmapper_genesis(v->dm)) < 0) {
176 LOGE("devmapper genesis failed for %s (%d)", mount_point, rc);
177 pthread_mutex_unlock(&v->lock);
178 return rc;
179 }
180 } else {
181 if ((rc = initialize_mbr(v->dev->disk)) < 0) {
182 LOGE("MBR init failed for %s (%d)", mount_point, rc);
183 pthread_mutex_unlock(&v->lock);
184 return rc;
185 }
186 }
187
188 volume_setstate(v, volstate_formatting);
189 pthread_mutex_unlock(&v->lock);
190 return rc;
191}
192
193int volmgr_bootstrap(void)
194{
195 int rc;
196
197 if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {
198 LOGE("Unable to process config");
199 return rc;
200 }
201
202 /*
203 * Check to see if any of our volumes is mounted
204 */
205 volume_t *v = vol_root;
206 while (v) {
207 if (_mountpoint_mounted(v->mount_point)) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700208 LOGW("Volume '%s' already mounted at startup", v->mount_point);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800209 v->state = volstate_mounted;
210 }
211 v = v->next;
212 }
213
214 return 0;
215}
216
217int volmgr_safe_mode(boolean enable)
218{
219 if (enable == safe_mode)
220 return 0;
221
222 safe_mode = enable;
223
224 volume_t *v = vol_root;
225 int rc;
226
227 while (v) {
228 pthread_mutex_lock(&v->lock);
229 if (v->state == volstate_mounted && v->fs) {
230 rc = v->fs->mount_fn(v->dev, v, safe_mode);
231 if (!rc) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700232 LOGI("Safe mode %s on %s", (enable ? "enabled" : "disabled"), v->mount_point);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800233 } else {
234 LOGE("Failed to %s safe-mode on %s (%s)",
235 (enable ? "enable" : "disable" ), v->mount_point, strerror(-rc));
236 }
237 }
238
239 pthread_mutex_unlock(&v->lock);
240 v = v->next;
241 }
242
243 return 0;
244}
245
246int volmgr_send_states(void)
247{
248 volume_t *vol_scan = vol_root;
249 int rc;
250
251 while (vol_scan) {
252 pthread_mutex_lock(&vol_scan->lock);
253 if ((rc = volume_send_state(vol_scan)) < 0) {
254 LOGE("Error sending state to framework (%d)", rc);
255 }
256 pthread_mutex_unlock(&vol_scan->lock);
257 vol_scan = vol_scan->next;
258 break; // XXX:
259 }
260
261 return 0;
262}
263
264/*
265 * Called when a block device is ready to be
266 * evaluated by the volume manager.
267 */
268int volmgr_consider_disk(blkdev_t *dev)
269{
270 volume_t *vol;
271
272 if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true)))
273 return 0;
274
275 pthread_mutex_lock(&vol->lock);
276
277 if (vol->state == volstate_mounted) {
278 LOGE("Volume %s already mounted (did we just crash?)", vol->mount_point);
279 pthread_mutex_unlock(&vol->lock);
280 return 0;
281 }
282
283 int rc = _volmgr_consider_disk_and_vol(vol, dev);
284 pthread_mutex_unlock(&vol->lock);
285 return rc;
286}
287
288int volmgr_start_volume_by_mountpoint(char *mount_point)
289{
290 volume_t *v;
291
292 v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
293 if (!v)
294 return -ENOENT;
295
296 if (v->media_type == media_devmapper) {
297 if (devmapper_start(v->dm) < 0) {
298 LOGE("volmgr failed to start devmapper volume '%s'",
299 v->mount_point);
300 }
301 } else if (v->media_type == media_mmc) {
302 if (!v->dev) {
303 LOGE("Cannot start volume '%s' (volume is not bound)", mount_point);
304 pthread_mutex_unlock(&v->lock);
305 return -ENOENT;
306 }
307
308 if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) {
309 LOGE("volmgr failed to start volume '%s'", v->mount_point);
310 }
311 }
312
313 pthread_mutex_unlock(&v->lock);
314 return 0;
315}
316
317static void _cb_volstopped_for_devmapper_teardown(volume_t *v, void *arg)
318{
319 devmapper_stop(v->dm);
320 volume_setstate(v, volstate_nomedia);
321 pthread_mutex_unlock(&v->lock);
322}
323
324int volmgr_stop_volume_by_mountpoint(char *mount_point)
325{
326 int rc;
327 volume_t *v;
328
329 v = volmgr_lookup_volume_by_mountpoint(mount_point, true);
330 if (!v)
331 return -ENOENT;
332
333 if (v->state == volstate_mounted)
334 volmgr_send_eject_request(v);
335
336 if (v->media_type == media_devmapper)
337 rc = volmgr_shutdown_volume(v, _cb_volstopped_for_devmapper_teardown, false);
338 else
339 rc = volmgr_shutdown_volume(v, NULL, true);
340
341 /*
342 * If shutdown returns -EINPROGRESS,
343 * do *not* release the lock as
344 * it is now owned by the reaper thread
345 */
346 if (rc != -EINPROGRESS) {
347 if (rc)
348 LOGE("unable to shutdown volume '%s'", v->mount_point);
349 pthread_mutex_unlock(&v->lock);
350 }
351 return 0;
352}
353
354int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *))
355{
356 LOG_VOL("Volmgr notified of %d:%d eject", dev->major, dev->minor);
357
358 volume_t *v;
359 int rc;
360
361 // XXX: Partitioning support is going to need us to stop *all*
362 // devices in this volume
363 if (!(v = volmgr_lookup_volume_by_dev(dev))) {
364 if (cb)
365 cb(dev);
366 return 0;
367 }
368
369 pthread_mutex_lock(&v->lock);
370
371 volume_state_t old_state = v->state;
372
373 if (v->state == volstate_mounted ||
374 v->state == volstate_ums ||
375 v->state == volstate_checking) {
376
377 volume_setstate(v, volstate_badremoval);
378
379 /*
380 * Stop any devmapper volumes which
381 * are using us as a source
382 * XXX: We may need to enforce stricter
383 * order here
384 */
385 volume_t *dmvol = vol_root;
386 while (dmvol) {
387 if ((dmvol->media_type == media_devmapper) &&
388 (dmvol->dm->src_type == dmsrc_loopback) &&
389 (!strncmp(dmvol->dm->type_data.loop.loop_src,
390 v->mount_point, strlen(v->mount_point)))) {
391
392 pthread_mutex_lock(&dmvol->lock);
393 if (dmvol->state != volstate_nomedia) {
394 rc = volmgr_shutdown_volume(dmvol, _cb_volstopped_for_devmapper_teardown, false);
395 if (rc != -EINPROGRESS) {
396 if (rc)
397 LOGE("unable to shutdown volume '%s'", v->mount_point);
398 pthread_mutex_unlock(&dmvol->lock);
399 }
400 } else
401 pthread_mutex_unlock(&dmvol->lock);
402 }
403 dmvol = dmvol->next;
404 }
405
406 } else if (v->state == volstate_formatting) {
407 /*
408 * The device is being ejected due to
409 * kernel disk revalidation.
410 */
411 LOG_VOL("Volmgr ignoring eject of %d:%d (volume formatting)",
412 dev->major, dev->minor);
413 if (cb)
414 cb(dev);
415 pthread_mutex_unlock(&v->lock);
416 return 0;
417 } else
418 volume_setstate(v, volstate_nomedia);
419
420 if (old_state == volstate_ums) {
421 ums_disable(v->ums_path);
422 pthread_mutex_unlock(&v->lock);
423 } else {
424 int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false);
425 if (rc != -EINPROGRESS) {
426 if (rc)
427 LOGE("unable to shutdown volume '%s'", v->mount_point);
428 pthread_mutex_unlock(&v->lock);
429 }
430 }
431 return 0;
432}
433
434static void _cb_volume_stopped_for_eject(volume_t *v, void *arg)
435{
436 void (* eject_cb) (blkdev_t *) = arg;
437
438#if DEBUG_VOLMGR
439 LOG_VOL("Volume %s has been stopped for eject", v->mount_point);
440#endif
441
442 if (eject_cb)
443 eject_cb(v->dev);
444 v->dev = NULL; // Clear dev because its being ejected
445}
446
447/*
448 * Instructs the volume manager to enable or disable USB mass storage
449 * on any volumes configured to use it.
450 */
451int volmgr_enable_ums(boolean enable)
452{
453 volume_t *v = vol_root;
454
455 while(v) {
456 if (v->ums_path) {
457 int rc;
458
459 if (enable) {
460 pthread_mutex_lock(&v->lock);
461 if (v->state == volstate_mounted)
462 volmgr_send_eject_request(v);
463 else if (v->state == volstate_ums) {
464 pthread_mutex_unlock(&v->lock);
465 goto next_vol;
466 }
467
468 // Stop the volume, and enable UMS in the callback
469 rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable, false);
470 if (rc != -EINPROGRESS) {
471 if (rc)
472 LOGE("unable to shutdown volume '%s'", v->mount_point);
473 pthread_mutex_unlock(&v->lock);
474 }
475 } else {
476 // Disable UMS
477 pthread_mutex_lock(&v->lock);
478 if (v->state != volstate_ums) {
479 pthread_mutex_unlock(&v->lock);
480 goto next_vol;
481 }
482
483 if ((rc = ums_disable(v->ums_path)) < 0) {
484 LOGE("unable to disable ums on '%s'", v->mount_point);
485 pthread_mutex_unlock(&v->lock);
486 continue;
487 }
488
489 LOG_VOL("Kick-starting volume %d:%d after UMS disable",
490 v->dev->disk->major, v->dev->disk->minor);
491 // Start volume
492 if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) {
493 LOGE("volmgr failed to consider disk %d:%d",
494 v->dev->disk->major, v->dev->disk->minor);
495 }
496 pthread_mutex_unlock(&v->lock);
497 }
498 }
499 next_vol:
500 v = v->next;
501 }
502 return 0;
503}
504
505/*
506 * Static functions
507 */
508
509static int volmgr_send_eject_request(volume_t *v)
510{
511 return send_msg_with_data(VOLD_EVT_EJECTING, v->mount_point);
512}
513
514// vol->lock must be held!
515static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev)
516{
517 int rc = 0;
518
519#if DEBUG_VOLMGR
520 LOG_VOL("volmgr_consider_disk_and_vol(%s, %d:%d):", vol->mount_point,
521 dev->major, dev->minor);
522#endif
523
524 if (vol->state == volstate_unknown ||
525 vol->state == volstate_mounted ||
San Mehatb6e70d82009-07-16 07:25:57 -0700526 vol->state == volstate_mounted_ro) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800527 LOGE("Cannot consider volume '%s' because it is in state '%d",
528 vol->mount_point, vol->state);
529 return -EADDRINUSE;
530 }
531
532 if (vol->state == volstate_formatting) {
533 LOG_VOL("Evaluating dev '%s' for formattable filesystems for '%s'",
534 dev->devpath, vol->mount_point);
535 /*
536 * Since we only support creating 1 partition (right now),
537 * we can just lookup the target by devno
538 */
539 blkdev_t *part = blkdev_lookup_by_devno(dev->major, 1);
540 if (!part) {
541 part = blkdev_lookup_by_devno(dev->major, 0);
542 if (!part) {
543 LOGE("Unable to find device to format");
544 return -ENODEV;
545 }
546 }
547
548 if ((rc = format_partition(part,
549 vol->media_type == media_devmapper ?
550 FORMAT_TYPE_EXT2 : FORMAT_TYPE_FAT32)) < 0) {
551 LOGE("format failed (%d)", rc);
552 return rc;
553 }
554
555 }
556
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700557 LOGI("Evaluating dev '%s' for mountable filesystems for '%s'",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800558 dev->devpath, vol->mount_point);
559
560 if (dev->nr_parts == 0) {
561 rc = _volmgr_start(vol, dev);
562#if DEBUG_VOLMGR
563 LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d", vol->mount_point,
564 dev->major, dev->minor, rc);
565#endif
566 } else {
567 /*
568 * Device has multiple partitions
569 * This is where interesting partition policies could be implemented.
570 * For now just try them in sequence until one succeeds
571 */
572
573 rc = -ENODEV;
574 int i;
575 for (i = 0; i < dev->nr_parts; i++) {
576 blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1));
577 if (!part) {
578 LOGE("Error - unable to lookup partition for blkdev %d:%d", dev->major, (i+1));
579 continue;
580 }
581 rc = _volmgr_start(vol, part);
582#if DEBUG_VOLMGR
583 LOG_VOL("_volmgr_start(%s, %d:%d) rc = %d",
584 vol->mount_point, part->major, part->minor, rc);
585#endif
San Mehat1f278212009-07-16 10:44:15 -0700586 if (!rc || rc == -EBUSY)
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800587 break;
588 }
589
590 if (rc == -ENODEV) {
591 // Assert to make sure each partition had a backing blkdev
592 LOGE("Internal consistency error");
593 return 0;
594 }
595 }
596
597 if (rc == -ENODATA) {
598 LOGE("Device %d:%d contains no usable filesystems",
599 dev->major, dev->minor);
600 rc = 0;
601 }
602
603 return rc;
604}
605
606static void volmgr_reaper_thread_sighandler(int signo)
607{
608 LOGE("Volume reaper thread got signal %d", signo);
609}
610
611static void __reaper_cleanup(void *arg)
612{
613 volume_t *vol = (volume_t *) arg;
614
615 if (vol->worker_args.reaper_args.cb)
616 vol->worker_args.reaper_args.cb(vol, vol->worker_args.reaper_args.cb_arg);
617
618 vol->worker_running = false;
619
620 // Wake up anyone that was waiting on this thread
621 pthread_mutex_unlock(&vol->worker_sem);
622
623 // Unlock the volume
624 pthread_mutex_unlock(&vol->lock);
625}
626
627static void *volmgr_reaper_thread(void *arg)
628{
629 volume_t *vol = (volume_t *) arg;
630
631 pthread_cleanup_push(__reaper_cleanup, arg);
632
633 vol->worker_running = true;
634 vol->worker_pid = getpid();
635
636 struct sigaction actions;
637
638 memset(&actions, 0, sizeof(actions));
639 sigemptyset(&actions.sa_mask);
640 actions.sa_flags = 0;
641 actions.sa_handler = volmgr_reaper_thread_sighandler;
642 sigaction(SIGUSR1, &actions, NULL);
643
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700644 LOGW("Reaper here - working on %s", vol->mount_point);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800645
646 boolean send_sig_kill = false;
647 int i, rc;
648
649 for (i = 0; i < 10; i++) {
650 errno = 0;
651 rc = umount(vol->mount_point);
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700652 LOGW("volmngr reaper umount(%s) attempt %d (%s)",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800653 vol->mount_point, i + 1, strerror(errno));
654 if (!rc)
655 break;
656 if (rc && (errno == EINVAL || errno == ENOENT)) {
657 rc = 0;
658 break;
659 }
660 sleep(1);
661 if (i >= 4) {
662 KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0);
663 if (!send_sig_kill)
664 send_sig_kill = true;
665 }
666 }
667
668 if (!rc) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700669 LOGI("Reaper sucessfully unmounted %s", vol->mount_point);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800670 vol->fs = NULL;
671 volume_setstate(vol, volstate_unmounted);
672 } else {
673 LOGE("Unable to unmount!! (%d)", rc);
674 }
675
676 out:
677 pthread_cleanup_pop(1);
678 pthread_exit(NULL);
679 return NULL;
680}
681
682// vol->lock must be held!
683static void volmgr_uncage_reaper(volume_t *vol, void (* cb) (volume_t *, void *arg), void *arg)
684{
685
686 if (vol->worker_running) {
687 LOGE("Worker thread is currently running.. waiting..");
688 pthread_mutex_lock(&vol->worker_sem);
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700689 LOGI("Worker thread now available");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800690 }
691
692 vol->worker_args.reaper_args.cb = cb;
693 vol->worker_args.reaper_args.cb_arg = arg;
694
695 pthread_attr_t attr;
696 pthread_attr_init(&attr);
697 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
698
699 pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol);
700}
701
702static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange)
703{
704 int i, rc;
705
706 if (v->state == volstate_mounted || v->state == volstate_badremoval) {
707 // Try to unmount right away (5 retries)
708 for (i = 0; i < 5; i++) {
709 rc = umount(v->mount_point);
710 if (!rc)
711 break;
712
713 if (rc && (errno == EINVAL || errno == ENOENT)) {
714 rc = 0;
715 break;
716 }
717
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700718 LOGI("volmngr quick stop umount(%s) attempt %d (%s)",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800719 v->mount_point, i + 1, strerror(errno));
720
721 if (i == 0)
722 usleep(1000 * 250); // First failure, sleep for 250 ms
723 else
724 sched_yield();
725 }
726
727 if (!rc) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700728 LOGI("volmgr_stop_volume(%s): Volume unmounted sucessfully",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800729 v->mount_point);
730 if (emit_statechange)
731 volume_setstate(v, volstate_unmounted);
732 v->fs = NULL;
733 goto out_cb_immed;
734 }
735
736 /*
737 * Since the volume is still in use, dispatch the stopping to
738 * a thread
739 */
The Android Open Source Projecte037fd72009-03-13 13:04:37 -0700740 LOGW("Volume %s is busy (%d) - uncaging the reaper", v->mount_point, rc);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800741 volmgr_uncage_reaper(v, cb, arg);
742 return -EINPROGRESS;
743 } else if (v->state == volstate_checking) {
744 volume_setstate(v, volstate_unmounted);
745 if (v->worker_running) {
746 LOG_VOL("Cancelling worker thread");
747 pthread_kill(v->worker_thread, SIGUSR1);
748 } else
749 LOGE("Strange... we were in checking state but worker thread wasn't running..");
750 goto out_cb_immed;
751 }
752
753 out_cb_immed:
754 if (cb)
755 cb(v, arg);
756 return 0;
757}
758
759
760/*
761 * Gracefully stop a volume
762 * v->lock must be held!
763 * if we return -EINPROGRESS, do NOT release the lock as the reaper
764 * is using the volume
765 */
766static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *), boolean emit_statechange)
767{
768 return volmgr_stop_volume(v, cb, NULL, emit_statechange);
769}
770
771static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg)
772{
773 void (* shutdown_cb) (volume_t *) = arg;
774
775#if DEBUG_VOLMGR
776 LOG_VOL("Volume %s has been stopped for shutdown", v->mount_point);
777#endif
778 shutdown_cb(v);
779}
780
781
782/*
783 * Called when a volume is sucessfully unmounted for UMS enable
784 */
785static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
786{
787 int rc;
788 char *devdir_path;
789
790#if DEBUG_VOLMGR
791 LOG_VOL("_cb_volstopped_for_ums_enable(%s):", v->mount_point);
792#endif
793 devdir_path = blkdev_get_devpath(v->dev->disk);
794
795 if ((rc = ums_enable(devdir_path, v->ums_path)) < 0) {
796 free(devdir_path);
797 LOGE("Error enabling ums (%d)", rc);
798 return;
799 }
800 free(devdir_path);
801 volume_setstate(v, volstate_ums);
802 pthread_mutex_unlock(&v->lock);
803}
804
805static int volmgr_readconfig(char *cfg_path)
806{
807 cnode *root = config_node("", "");
808 cnode *node;
809
810 config_load_file(root, cfg_path);
811 node = root->first_child;
812
813 while (node) {
814 if (!strncmp(node->name, "volume_", 7))
815 volmgr_config_volume(node);
816 else
817 LOGE("Skipping unknown configuration node '%s'", node->name);
818 node = node->next;
819 }
820 return 0;
821}
822
823static void volmgr_add_mediapath_to_volume(volume_t *v, char *media_path)
824{
825 int i;
826
827#if DEBUG_VOLMGR
828 LOG_VOL("volmgr_add_mediapath_to_volume(%p, %s):", v, media_path);
829#endif
830 for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
831 if (!v->media_paths[i]) {
832 v->media_paths[i] = strdup(media_path);
833 return;
834 }
835 }
836 LOGE("Unable to add media path '%s' to volume (out of media slots)", media_path);
837}
838
839static int volmgr_config_volume(cnode *node)
840{
841 volume_t *new;
842 int rc = 0, i;
843
844 char *dm_src, *dm_src_type, *dm_tgt, *dm_param, *dm_tgtfs;
845 uint32_t dm_size_mb = 0;
846
847 dm_src = dm_src_type = dm_tgt = dm_param = dm_tgtfs = NULL;
848#if DEBUG_VOLMGR
849 LOG_VOL("volmgr_configure_volume(%s):", node->name);
850#endif
851 if (!(new = malloc(sizeof(volume_t))))
852 return -ENOMEM;
853 memset(new, 0, sizeof(volume_t));
854
855 new->state = volstate_nomedia;
856 pthread_mutex_init(&new->lock, NULL);
857 pthread_mutex_init(&new->worker_sem, NULL);
858
859 cnode *child = node->first_child;
860
861 while (child) {
862 if (!strcmp(child->name, "media_path"))
863 volmgr_add_mediapath_to_volume(new, child->value);
864 else if (!strcmp(child->name, "emu_media_path"))
865 volmgr_add_mediapath_to_volume(new, child->value);
866 else if (!strcmp(child->name, "media_type")) {
867 if (!strcmp(child->value, "mmc"))
868 new->media_type = media_mmc;
869 else if (!strcmp(child->value, "devmapper"))
870 new->media_type = media_devmapper;
871 else {
872 LOGE("Invalid media type '%s'", child->value);
873 rc = -EINVAL;
874 goto out_free;
875 }
876 } else if (!strcmp(child->name, "mount_point"))
877 new->mount_point = strdup(child->value);
878 else if (!strcmp(child->name, "ums_path"))
879 new->ums_path = strdup(child->value);
880 else if (!strcmp(child->name, "dm_src"))
881 dm_src = strdup(child->value);
882 else if (!strcmp(child->name, "dm_src_type"))
883 dm_src_type = strdup(child->value);
884 else if (!strcmp(child->name, "dm_src_size_mb"))
885 dm_size_mb = atoi(child->value);
886 else if (!strcmp(child->name, "dm_target"))
887 dm_tgt = strdup(child->value);
888 else if (!strcmp(child->name, "dm_target_params"))
889 dm_param = strdup(child->value);
890 else if (!strcmp(child->name, "dm_target_fs"))
891 dm_tgtfs = strdup(child->value);
892 else
893 LOGE("Ignoring unknown config entry '%s'", child->name);
894 child = child->next;
895 }
896
897 if (new->media_type == media_mmc) {
898 if (!new->media_paths[0] || !new->mount_point || new->media_type == media_unknown) {
899 LOGE("Required configuration parameter missing for mmc volume");
900 rc = -EINVAL;
901 goto out_free;
902 }
903 } else if (new->media_type == media_devmapper) {
904 if (!dm_src || !dm_src_type || !dm_tgt ||
905 !dm_param || !dm_tgtfs || !dm_size_mb) {
906 LOGE("Required configuration parameter missing for devmapper volume");
907 rc = -EINVAL;
908 goto out_free;
909 }
910
911 char dm_mediapath[255];
912 if (!(new->dm = devmapper_init(dm_src, dm_src_type, dm_size_mb,
913 dm_tgt, dm_param, dm_tgtfs, dm_mediapath))) {
914 LOGE("Unable to initialize devmapping");
915 goto out_free;
916 }
917 LOG_VOL("media path for devmapper volume = '%s'", dm_mediapath);
918 volmgr_add_mediapath_to_volume(new, dm_mediapath);
919 }
920
921 if (!vol_root)
922 vol_root = new;
923 else {
924 volume_t *scan = vol_root;
925 while (scan->next)
926 scan = scan->next;
927 scan->next = new;
928 }
929
930 if (dm_src)
931 free(dm_src);
932 if (dm_src_type)
933 free(dm_src_type);
934 if (dm_tgt)
935 free(dm_tgt);
936 if (dm_param)
937 free(dm_param);
938 if (dm_tgtfs)
939 free(dm_tgtfs);
940
941 return rc;
942
943 out_free:
944
945 if (dm_src)
946 free(dm_src);
947 if (dm_src_type)
948 free(dm_src_type);
949 if (dm_tgt)
950 free(dm_tgt);
951 if (dm_param)
952 free(dm_param);
953 if (dm_tgtfs)
954 free(dm_tgtfs);
955
956
957 for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
958 if (new->media_paths[i])
959 free(new->media_paths[i]);
960 }
961 if (new->mount_point)
962 free(new->mount_point);
963 if (new->ums_path)
964 free(new->ums_path);
965 return rc;
966}
967
968static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev)
969{
970 volume_t *scan = vol_root;
971 while(scan) {
972 if (scan->dev == dev)
973 return scan;
974 scan = scan->next;
975 }
976 return NULL;
977}
978
979static volume_t *volmgr_lookup_volume_by_mountpoint(char *mount_point, boolean leave_locked)
980{
981 volume_t *v = vol_root;
982
983 while(v) {
984 pthread_mutex_lock(&v->lock);
985 if (!strcmp(v->mount_point, mount_point)) {
986 if (!leave_locked)
987 pthread_mutex_unlock(&v->lock);
988 return v;
989 }
990 pthread_mutex_unlock(&v->lock);
991 v = v->next;
992 }
993 return NULL;
994}
995
996static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy)
997{
998 volume_t *scan = vol_root;
999 int i;
1000
1001 while (scan) {
1002
1003 for (i = 0; i < VOLMGR_MAX_MEDIAPATHS_PER_VOLUME; i++) {
1004 if (!scan->media_paths[i])
1005 continue;
1006
1007 if (fuzzy && !strncmp(media_path, scan->media_paths[i], strlen(scan->media_paths[i])))
1008 return scan;
1009 else if (!fuzzy && !strcmp(media_path, scan->media_paths[i]))
1010 return scan;
1011 }
1012
1013 scan = scan->next;
1014 }
1015 return NULL;
1016}
1017
1018/*
1019 * Attempt to bring a volume online
1020 * Returns: 0 on success, errno on failure, with the following exceptions:
1021 * - ENODATA - Unsupported filesystem type / blank
1022 * vol->lock MUST be held!
1023 */
1024static int _volmgr_start(volume_t *vol, blkdev_t *dev)
1025{
1026 struct volmgr_fstable_entry *fs;
1027 int rc = ENODATA;
1028
1029#if DEBUG_VOLMGR
1030 LOG_VOL("_volmgr_start(%s, %d:%d):", vol->mount_point,
1031 dev->major, dev->minor);
1032#endif
1033
1034 if (vol->state == volstate_mounted) {
1035 LOGE("Unable to start volume '%s' (already mounted)", vol->mount_point);
1036 return -EBUSY;
1037 }
1038
1039 for (fs = fs_table; fs->name; fs++) {
1040 if (!fs->identify_fn(dev))
1041 break;
1042 }
1043
1044 if (!fs) {
1045 LOGE("No supported filesystems on %d:%d", dev->major, dev->minor);
1046 volume_setstate(vol, volstate_nofs);
1047 return -ENODATA;
1048 }
1049
1050 return volmgr_start_fs(fs, vol, dev);
1051}
1052
1053// vol->lock MUST be held!
1054static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev)
1055{
1056 /*
1057 * Spawn a thread to do the actual checking / mounting in
1058 */
1059
1060 if (vol->worker_running) {
1061 LOGE("Worker thread is currently running.. waiting..");
1062 pthread_mutex_lock(&vol->worker_sem);
The Android Open Source Projecte037fd72009-03-13 13:04:37 -07001063 LOGI("Worker thread now available");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001064 }
1065
1066 vol->dev = dev;
1067
San Mehat1f278212009-07-16 10:44:15 -07001068 if (bootstrap) {
1069 LOGI("Aborting start of %s (bootstrap = %d)\n", vol->mount_point,
1070 bootstrap);
1071 vol->state = volstate_unmounted;
1072 return -EBUSY;
1073 }
1074
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001075 vol->worker_args.start_args.fs = fs;
1076 vol->worker_args.start_args.dev = dev;
1077
1078 pthread_attr_t attr;
1079 pthread_attr_init(&attr);
1080 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1081
1082 pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol);
1083
1084 return 0;
1085}
1086
1087static void __start_fs_thread_lock_cleanup(void *arg)
1088{
1089 volume_t *vol = (volume_t *) arg;
1090
1091#if DEBUG_VOLMGR
1092 LOG_VOL("__start_fs_thread_lock_cleanup(%s):", vol->mount_point);
1093#endif
1094
1095 vol->worker_running = false;
1096
1097 // Wake up anyone that was waiting on this thread
1098 pthread_mutex_unlock(&vol->worker_sem);
1099
1100 // Unlock the volume
1101 pthread_mutex_unlock(&vol->lock);
1102}
1103
1104static void *volmgr_start_fs_thread(void *arg)
1105{
1106 volume_t *vol = (volume_t *) arg;
1107
1108 pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg);
1109 pthread_mutex_lock(&vol->lock);
1110
1111 vol->worker_running = true;
1112 vol->worker_pid = getpid();
1113
1114 struct sigaction actions;
1115
1116 memset(&actions, 0, sizeof(actions));
1117 sigemptyset(&actions.sa_mask);
1118 actions.sa_flags = 0;
1119 actions.sa_handler = volmgr_start_fs_thread_sighandler;
1120 sigaction(SIGUSR1, &actions, NULL);
1121
1122 struct volmgr_fstable_entry *fs = vol->worker_args.start_args.fs;
1123 blkdev_t *dev = vol->worker_args.start_args.dev;
1124 int rc;
1125
1126#if DEBUG_VOLMGR
1127 LOG_VOL("Worker thread pid %d starting %s fs %d:%d on %s", getpid(),
1128 fs->name, dev->major, dev->minor, vol->mount_point);
1129#endif
1130
1131 if (fs->check_fn) {
1132#if DEBUG_VOLMGR
1133 LOG_VOL("Starting %s filesystem check on %d:%d", fs->name,
1134 dev->major, dev->minor);
1135#endif
1136 volume_setstate(vol, volstate_checking);
1137 pthread_mutex_unlock(&vol->lock);
1138 rc = fs->check_fn(dev);
1139 pthread_mutex_lock(&vol->lock);
1140 if (vol->state != volstate_checking) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -07001141 LOGE("filesystem check aborted");
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001142 goto out;
1143 }
1144
1145 if (rc < 0) {
1146 LOGE("%s filesystem check failed on %d:%d (%s)", fs->name,
1147 dev->major, dev->minor, strerror(-rc));
1148 if (rc == -ENODATA) {
1149 volume_setstate(vol, volstate_nofs);
1150 goto out;
1151 }
1152 goto out_unmountable;
1153 }
1154#if DEBUG_VOLMGR
The Android Open Source Projecte037fd72009-03-13 13:04:37 -07001155 LOGI("%s filesystem check of %d:%d OK", fs->name,
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001156 dev->major, dev->minor);
1157#endif
1158 }
1159
1160 rc = fs->mount_fn(dev, vol, safe_mode);
1161 if (!rc) {
The Android Open Source Projecte037fd72009-03-13 13:04:37 -07001162 LOGI("Sucessfully mounted %s filesystem %d:%d on %s (safe-mode %s)",
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001163 fs->name, dev->major, dev->minor, vol->mount_point,
1164 (safe_mode ? "on" : "off"));
1165 vol->fs = fs;
1166 volume_setstate(vol, volstate_mounted);
1167 goto out;
1168 }
1169
1170 LOGE("%s filesystem mount of %d:%d failed (%d)", fs->name, dev->major,
1171 dev->minor, rc);
1172
1173 out_unmountable:
1174 volume_setstate(vol, volstate_damaged);
1175 out:
1176 pthread_cleanup_pop(1);
1177 pthread_exit(NULL);
1178 return NULL;
1179}
1180
1181static void volmgr_start_fs_thread_sighandler(int signo)
1182{
1183 LOGE("Volume startup thread got signal %d", signo);
1184}
1185
1186static void volume_setstate(volume_t *vol, volume_state_t state)
1187{
1188 if (state == vol->state)
1189 return;
1190
1191#if DEBUG_VOLMGR
1192 LOG_VOL("Volume %s state change from %d -> %d", vol->mount_point, vol->state, state);
1193#endif
1194
1195 vol->state = state;
1196
1197 char *prop_val = conv_volstate_to_propstr(vol->state);
1198
1199 if (prop_val) {
1200 property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val);
1201 volume_send_state(vol);
1202 }
1203}
1204
1205static int volume_send_state(volume_t *vol)
1206{
1207 char *event = conv_volstate_to_eventstr(vol->state);
1208
1209 return send_msg_with_data(event, vol->mount_point);
1210}
1211
1212static char *conv_volstate_to_eventstr(volume_state_t state)
1213{
1214 int i;
1215
1216 for (i = 0; volume_state_strings[i].event != NULL; i++) {
1217 if (volume_state_strings[i].state == state)
1218 break;
1219 }
1220
1221 return volume_state_strings[i].event;
1222}
1223
1224static char *conv_volstate_to_propstr(volume_state_t state)
1225{
1226 int i;
1227
1228 for (i = 0; volume_state_strings[i].event != NULL; i++) {
1229 if (volume_state_strings[i].state == state)
1230 break;
1231 }
1232
1233 return volume_state_strings[i].property_val;
1234}
1235