blob: b162ff5a669102857db052a81b12986b5afd9f0f [file] [log] [blame]
The Android Open Source Project8ac3a132009-01-20 14:04:01 -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
35#include "volmgr_ext3.h"
36#include "volmgr_vfat.h"
37
38#define DEBUG_VOLMGR 0
39
40static volume_t *vol_root = NULL;
41
42static struct volmgr_fstable_entry fs_table[] = {
43 { "ext3", ext3_identify, ext3_check, ext3_mount },
44 { "vfat", vfat_identify, vfat_check, vfat_mount },
45 { NULL, NULL, NULL, NULL }
46};
47
48struct _volume_state_event_map {
49 volume_state_t state;
50 char *event;
51 char *property_val;
52};
53
54static struct _volume_state_event_map volume_state_strings[] = {
55 { volstate_unknown, "volstate_unknown:", "unknown" },
56 { volstate_nomedia, VOLD_EVT_NOMEDIA, VOLD_ES_PVAL_NOMEDIA },
57 { volstate_unmounted, VOLD_EVT_UNMOUNTED, VOLD_ES_PVAL_UNMOUNTED },
58 { volstate_checking, VOLD_EVT_CHECKING, VOLD_ES_PVAL_CHECKING },
59 { volstate_mounted, VOLD_EVT_MOUNTED, VOLD_ES_PVAL_MOUNTED },
60 { volstate_mounted_ro, VOLD_EVT_MOUNTED_RO, VOLD_ES_PVAL_MOUNTED_RO },
61 { volstate_badremoval, VOLD_EVT_BADREMOVAL, VOLD_ES_PVAL_BADREMOVAL },
62 { volstate_damaged, VOLD_EVT_DAMAGED, VOLD_ES_PVAL_DAMAGED },
63 { volstate_nofs, VOLD_EVT_NOFS, VOLD_ES_PVAL_NOFS },
64 { volstate_ums, VOLD_EVT_UMS, VOLD_ES_PVAL_UMS },
65 { 0, NULL, NULL }
66};
67
68
69static int volmgr_readconfig(char *cfg_path);
70static int volmgr_config_volume(cnode *node);
71static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy);
72static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev);
73static int _volmgr_start(volume_t *vol, blkdev_t *dev);
74static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev);
75static void *volmgr_start_fs_thread(void *arg);
76static void volmgr_start_fs_thread_sighandler(int signo);
77static void volume_setstate(volume_t *vol, volume_state_t state);
78static char *conv_volstate_to_eventstr(volume_state_t state);
79static char *conv_volstate_to_propstr(volume_state_t state);
80static int volume_send_state(volume_t *vol);
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -080081static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -080082static int _volmgr_enable_ums(volume_t *);
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -080083static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *arg));
The Android Open Source Project8ac3a132009-01-20 14:04:01 -080084static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, int emit_statechange);
85static void _cb_volume_stopped_for_eject(volume_t *v, void *arg);
86static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg);
87static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev);
88static void volmgr_uncage_reaper(volume_t *vol);
89static void volmgr_reaper_thread_sighandler(int signo);
90
91/*
92 * Public functions
93 */
94int volmgr_bootstrap(void)
95{
96 int rc;
97
98 if ((rc = volmgr_readconfig("/system/etc/vold.conf")) < 0) {
99 LOGE("Unable to process config\n");
100 return rc;
101 }
102
103 return 0;
104}
105
106int volmgr_send_states(void)
107{
108 volume_t *vol_scan = vol_root;
109 int rc;
110
111 while (vol_scan) {
112 pthread_mutex_lock(&vol_scan->lock);
113 if ((rc = volume_send_state(vol_scan)) < 0) {
114 LOGE("Error sending state to framework (%d)\n", rc);
115 }
116 pthread_mutex_unlock(&vol_scan->lock);
117 vol_scan = vol_scan->next;
118 }
119
120 return 0;
121}
122
123/*
124 * Called when a block device is ready to be
125 * evaluated by the volume manager.
126 */
127int volmgr_consider_disk(blkdev_t *dev)
128{
129 volume_t *vol;
130
131 if (!(vol = volmgr_lookup_volume_by_mediapath(dev->media->devpath, true))) {
132 LOG_VOL("volmgr ignoring '%s' - no matching volume found\n", dev->media->devpath);
133 return 0;
134 }
135
136 pthread_mutex_lock(&vol->lock);
137 int rc = _volmgr_consider_disk_and_vol(vol, dev);
138 pthread_mutex_unlock(&vol->lock);
139 return rc;
140}
141
142int volmgr_start_volume_by_mountpoint(char *mount_point)
143{
144 volume_t *v = vol_root;
145
146 while(v) {
147 if (!strcmp(v->mount_point, mount_point)) {
148 pthread_mutex_lock(&v->lock);
149 if (!v->dev) {
150 LOGE("Cannot start volume '%s' (volume is not bound to a blkdev)\n", mount_point);
151 pthread_mutex_unlock(&v->lock);
152 return -ENOENT;
153 }
154
155 if (_volmgr_consider_disk_and_vol(v, v->dev->disk) < 0) {
156 LOGE("volmgr failed to start volume '%s'\n", v->mount_point);
157 }
158 pthread_mutex_unlock(&v->lock);
159 return 0;
160 }
161 v = v->next;
162 }
163
164 return -ENOENT;
165}
166
167int volmgr_stop_volume_by_mountpoint(char *mount_point)
168{
169 volume_t *v = vol_root;
170
171 while(v) {
172 if (!strcmp(v->mount_point, mount_point)) {
173 pthread_mutex_lock(&v->lock);
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800174 if (volmgr_shutdown_volume(v, NULL) < 0)
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800175 LOGE("unable to shutdown volume '%s'\n", v->mount_point);
176 pthread_mutex_unlock(&v->lock);
177 return 0;
178 }
179 v = v->next;
180 }
181
182 return -ENOENT;
183}
184
185int volmgr_notify_eject(blkdev_t *dev, void (* cb) (blkdev_t *))
186{
187#if DEBUG_VOLMGR
188 LOG_VOL("volmgr_notify_eject(%s)\n", dev->dev_fspath);
189#endif
190
191 volume_t *v;
192
193 // XXX: Partitioning support is going to need us to stop *all*
194 // devices in this volume
195 if (!(v = volmgr_lookup_volume_by_dev(dev))) {
196 if (cb)
197 cb(dev);
198 return 0;
199 }
200
201 pthread_mutex_lock(&v->lock);
202 if (v->state == volstate_mounted)
203 volume_setstate(v, volstate_badremoval);
204
205 int rc = volmgr_stop_volume(v, _cb_volume_stopped_for_eject, cb, false);
206
207 pthread_mutex_unlock(&v->lock);
208 return rc;
209}
210
211static void _cb_volume_stopped_for_eject(volume_t *v, void *arg)
212{
213 void (* eject_cb) (blkdev_t *) = arg;
214 LOG_VOL("Volume %s has been stopped for eject\n", v->mount_point);
215
216 eject_cb(v->dev);
217 v->dev = NULL; // Clear dev because its being ejected
218}
219
220/*
221 * Instructs the volume manager to enable or disable USB mass storage
222 * on any volumes configured to use it.
223 */
224int volmgr_enable_ums(boolean enable)
225{
226 volume_t *v = vol_root;
227
228 while(v) {
229 if (v->ums_path) {
230 int rc;
231
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800232 if (enable) {
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800233 pthread_mutex_lock(&v->lock);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800234 // Stop the volume, and enable UMS in the callback
235 if ((rc = volmgr_shutdown_volume(v, _cb_volstopped_for_ums_enable)) < 0)
236 LOGE("unable to shutdown volume '%s'\n", v->mount_point);
237 } else {
238 // Disable UMS
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800239 pthread_mutex_lock(&v->lock);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800240 if ((rc = ums_disable(v->ums_path)) < 0) {
241 LOGE("unable to disable ums on '%s'\n", v->mount_point);
242 pthread_mutex_unlock(&v->lock);
243 continue;
244 }
245 volume_setstate(v, volstate_unmounted);
246
247 LOG_VOL("Kick-starting volume '%s' after UMS disable\n", v->dev->disk->dev_fspath);
248 // Start volume
249 if ((rc = _volmgr_consider_disk_and_vol(v, v->dev->disk)) < 0) {
250 LOGE("volmgr failed to consider disk '%s'\n", v->dev->disk->dev_fspath);
251 }
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800252 pthread_mutex_unlock(&v->lock);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800253 }
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800254 }
255 v = v->next;
256 }
257 return 0;
258}
259
260/*
261 * Static functions
262 */
263
264// vol->lock must be held!
265static int _volmgr_consider_disk_and_vol(volume_t *vol, blkdev_t *dev)
266{
267 int rc = 0;
268
269#if DEBUG_VOLMGR
270 LOG_VOL("volmgr_consider_disk_and_vol(%s, %s):\n", vol->mount_point, dev->dev_fspath);
271#endif
272
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800273 if (vol->state != volstate_nomedia &&
274 vol->state != volstate_unmounted &&
275 vol->state != volstate_badremoval) {
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800276 LOGE("Volume manager is already handling volume '%s' (currently in state %d)\n", vol->mount_point, vol->state);
277 return -EADDRINUSE;
278 }
279
280 volume_setstate(vol, volstate_unmounted);
281
282 LOG_VOL("Evaluating dev '%s' for mountable filesystems for '%s'\n", dev->devpath, vol->mount_point);
283
284 if (dev->nr_parts == 0) {
285 rc = _volmgr_start(vol, dev);
286#if DEBUG_VOLMGR
287 LOG_VOL("_volmgr_start(%s, %s) rc = %d\n", vol->mount_point, dev->dev_fspath ,rc);
288#endif
289 } else {
290 /*
291 * Device has multiple partitions
292 * This is where interesting partition policies could be implemented.
293 * For now just try them in sequence until one succeeds
294 */
295
296 rc = -ENODEV;
297 int i;
298 for (i = 0; i < dev->nr_parts; i++) {
299 blkdev_t *part = blkdev_lookup_by_devno(dev->major, (i+1));
300 if (!part) {
301 LOGE("Error - unable to lookup partition for blkdev %d:%d\n", dev->major, (i+1));
302 continue;
303 }
304 rc = _volmgr_start(vol, part);
305#if DEBUG_VOLMGR
306 LOG_VOL("_volmgr_start(%s, %s) rc = %d\n", vol->mount_point, part->dev_fspath, rc);
307#endif
308 if (!rc)
309 break;
310 }
311
312 if (rc == -ENODEV) {
313 // Assert to make sure each partition had a backing blkdev
314 LOGE("Internal consistency error\n");
315 return 0;
316 }
317 }
318
319 if (rc == -ENODATA) {
320 LOGE("Device %s contains no usable filesystems\n", dev->dev_fspath);
321 rc = 0;
322 }
323
324 return rc;
325}
326
327static void volmgr_reaper_thread_sighandler(int signo)
328{
329 LOGE("volmgr reaper thread got signal %d\n", signo);
330}
331
332static void __reaper_cleanup(void *arg)
333{
334 volume_t *vol = (volume_t *) arg;
335
336 LOG_VOL("__reaper_cleanup(%s):\n", vol->mount_point);
337
338 vol->worker_running = false;
339
340 // Wake up anyone that was waiting on this thread
341 pthread_mutex_unlock(&vol->worker_sem);
342
343 // Unlock the volume
344 pthread_mutex_unlock(&vol->lock);
345}
346
347static void *volmgr_reaper_thread(void *arg)
348{
349 volume_t *vol = (volume_t *) arg;
350
351 pthread_cleanup_push(__reaper_cleanup, arg);
352 pthread_mutex_lock(&vol->lock);
353
354 vol->worker_running = true;
355 vol->worker_pid = getpid();
356
357 struct sigaction actions;
358
359 memset(&actions, 0, sizeof(actions));
360 sigemptyset(&actions.sa_mask);
361 actions.sa_flags = 0;
362 actions.sa_handler = volmgr_reaper_thread_sighandler;
363 sigaction(SIGUSR1, &actions, NULL);
364
365 LOG_VOL("Worker thread pid %d reaping %s\n", getpid(), vol->mount_point);
366
367 boolean send_sig_kill = false;
368 int i, rc;
369
370 for (i = 0; i < 10; i++) {
371 rc = umount(vol->mount_point);
372 LOG_VOL("volmngr reaper umount(%s) attempt %d rc = %d\n",
373 vol->mount_point, i + 1, rc);
374 if (!rc)
375 break;
376 if (rc && (errno == EINVAL || errno == ENOENT)) {
377 rc = 0;
378 break;
379 }
380 KillProcessesWithOpenFiles(vol->mount_point, send_sig_kill, NULL, 0);
381 sleep(1);
382 if (!send_sig_kill)
383 send_sig_kill = true;
384 }
385
386 if (!rc) {
387 LOG_VOL("Reaper sucessfully unmounted %s\n", vol->mount_point);
388 volume_setstate(vol, volstate_unmounted);
389 } else {
390 LOGE("Unable to unmount!! (%d)\n", rc);
391 }
392
393 out:
394 pthread_cleanup_pop(1);
395 pthread_exit(NULL);
396 return NULL;
397}
398
399static void volmgr_uncage_reaper(volume_t *vol)
400{
401 if (vol->worker_running) {
402 LOGE("Worker thread is currently running.. waiting..\n");
403 pthread_mutex_lock(&vol->worker_sem);
404 LOG_VOL("Worker thread now available\n");
405 }
406
407 vol->worker_args.fs = NULL;
408 vol->worker_args.dev = NULL;
409
410 pthread_attr_t attr;
411 pthread_attr_init(&attr);
412 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
413
414 pthread_create(&vol->worker_thread, &attr, volmgr_reaper_thread, vol);
415}
416
417static int volmgr_stop_volume(volume_t *v, void (*cb) (volume_t *, void *), void *arg, boolean emit_statechange)
418{
419 int i, rc;
420
421 if (v->state == volstate_mounted || v->state == volstate_badremoval) {
422 // Try to unmount right away (5 retries)
423 for (i = 0; i < 5; i++) {
424 rc = umount(v->mount_point);
425 LOG_VOL("volmngr quick stop umount(%s) attempt %d rc = %d\n",
426 v->mount_point, i + 1, rc);
427 if (!rc)
428 break;
429 if (rc && (errno == EINVAL || errno == ENOENT)) {
430 rc = 0;
431 break;
432 }
433 sched_yield();
434 }
435
436 if (!rc) {
437 LOG_VOL("volmgr_stop_volume(%s): Volume unmounted sucessfully\n",
438 v->mount_point);
439 if (emit_statechange)
440 volume_setstate(v, volstate_unmounted);
441 goto out_cb_immed;
442 }
443
444 /*
445 * Since the volume is still in use, dispatch the stopping to
446 * a thread
447 */
448 LOG_VOL("Volume %s is busy (%d) - uncaging the reaper\n", v->mount_point, rc);
449 volmgr_uncage_reaper(v);
450 return -EINPROGRESS;
451 } else if (v->state == volstate_checking) {
452 volume_setstate(v, volstate_unmounted);
453 if (v->worker_running) {
454 LOG_VOL("Cancelling worker thread\n");
455 pthread_kill(v->worker_thread, SIGUSR1);
456 } else
457 LOGE("Strange... we were in checking state but worker thread wasn't running..\n");
458 goto out_cb_immed;
459 }
460
461 LOGE("volmgr: nothing to do to stop vol '%s' (in state %d)\n",
462 v->mount_point, v->state);
463 out_cb_immed:
464 if (cb)
465 cb(v, arg);
466 return 0;
467}
468
469
470/*
471 * Gracefully stop a volume
472 */
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800473static int volmgr_shutdown_volume(volume_t *v, void (* cb) (volume_t *, void *))
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800474{
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800475 return volmgr_stop_volume(v, cb, NULL, true);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800476}
477
478static void _cb_volume_stopped_for_shutdown(volume_t *v, void *arg)
479{
480 void (* shutdown_cb) (volume_t *) = arg;
481
482 LOG_VOL("Volume %s has been stopped for shutdown\n", v->mount_point);
483 shutdown_cb(v);
484}
485
486/*
487 * Called when a volume is sucessfully unmounted for UMS enable
488 */
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800489static void _cb_volstopped_for_ums_enable(volume_t *v, void *arg)
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800490{
491 int rc;
492
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800493 LOG_VOL("_cb_volstopped_for_ums_enable(%s):\n", v->dev->dev_fspath);
494 if ((rc = ums_enable(v->dev->disk->dev_fspath, v->ums_path)) < 0) {
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800495 LOGE("Error enabling ums (%d)\n", rc);
496 return;
497 }
498 volume_setstate(v, volstate_ums);
The Android Open Source Projectdcf3ce22009-01-22 00:13:45 -0800499 pthread_mutex_unlock(&v->lock);
The Android Open Source Project8ac3a132009-01-20 14:04:01 -0800500}
501
502static int volmgr_readconfig(char *cfg_path)
503{
504 cnode *root = config_node("", "");
505 cnode *node;
506
507 config_load_file(root, cfg_path);
508 node = root->first_child;
509
510 while (node) {
511 if (!strcmp(node->name, "volume"))
512 volmgr_config_volume(node);
513 else
514 LOGE("Skipping unknown configuration node '%s'\n", node->name);
515 node = node->next;
516 }
517 return 0;
518}
519
520static int volmgr_config_volume(cnode *node)
521{
522 volume_t *new;
523 int rc = 0;
524
525 if (!(new = malloc(sizeof(volume_t))))
526 return -ENOMEM;
527 memset(new, 0, sizeof(volume_t));
528
529 new->state = volstate_nomedia;
530 pthread_mutex_init(&new->lock, NULL);
531 pthread_mutex_init(&new->worker_sem, NULL);
532
533 cnode *child = node->first_child;
534
535 while (child) {
536 if (!strcmp(child->name, "media_path"))
537 new->media_path = strdup(child->value);
538 else if (!strcmp(child->name, "media_type")) {
539 if (!strcmp(child->value, "mmc"))
540 new->media_type = media_mmc;
541 else {
542 LOGE("Invalid media type '%s'\n", child->value);
543 rc = -EINVAL;
544 goto out_free;
545 }
546 } else if (!strcmp(child->name, "mount_point"))
547 new->mount_point = strdup(child->value);
548 else if (!strcmp(child->name, "ums_path"))
549 new->ums_path = strdup(child->value);
550 else
551 LOGE("Ignoring unknown config entry '%s'\n", child->name);
552 child = child->next;
553 }
554
555 if (!new->media_path || !new->mount_point || new->media_type == media_unknown) {
556 LOGE("Required configuration parameter missing for volume\n");
557 rc = -EINVAL;
558 goto out_free;
559 }
560
561 if (!vol_root)
562 vol_root = new;
563 else {
564 volume_t *scan = vol_root;
565 while (scan->next)
566 scan = scan->next;
567 scan->next = new;
568 }
569
570 return rc;
571
572 out_free:
573 if (new->media_path)
574 free(new->media_path);
575 if (new->mount_point)
576 free(new->mount_point);
577 if (new->ums_path)
578 free(new->ums_path);
579 return rc;
580}
581
582static volume_t *volmgr_lookup_volume_by_dev(blkdev_t *dev)
583{
584 volume_t *scan = vol_root;
585 while(scan) {
586 if (scan->dev == dev)
587 return scan;
588 scan = scan->next;
589 }
590 return NULL;
591}
592
593static volume_t *volmgr_lookup_volume_by_mediapath(char *media_path, boolean fuzzy)
594{
595 volume_t *scan = vol_root;
596 volume_t *res = NULL;
597
598 while (scan) {
599 if (fuzzy) {
600 if (!strncmp(media_path, scan->media_path, strlen(scan->media_path))) {
601 if (!res)
602 res = scan;
603 else {
604 LOGE("Warning - multiple matching volumes for media '%s' - using first\n", media_path);
605 break;
606 }
607 }
608 } else if (!strcmp(media_path, scan->media_path))
609 return scan;
610
611 scan = scan->next;
612 }
613 return res;
614}
615
616/*
617 * Attempt to bring a volume online
618 * Returns: 0 on success, errno on failure, with the following exceptions:
619 * - ENODATA - Unsupported filesystem type / blank
620 * vol->lock MUST be held!
621 */
622static int _volmgr_start(volume_t *vol, blkdev_t *dev)
623{
624 struct volmgr_fstable_entry *fs;
625 int rc = ENODATA;
626
627#if DEBUG_VOLMGR
628 LOG_VOL("_volmgr_start(%s, %s):\n", vol->mount_point, dev->dev_fspath);
629#endif
630
631 for (fs = fs_table; fs->name; fs++) {
632 if (!fs->identify_fn(dev))
633 break;
634 }
635
636 if (!fs) {
637 LOGE("No supported filesystems on %s\n", dev->dev_fspath);
638 volume_setstate(vol, volstate_nofs);
639 return -ENODATA;
640 }
641
642 return volmgr_start_fs(fs, vol, dev);
643}
644
645// vol->lock MUST be held!
646static int volmgr_start_fs(struct volmgr_fstable_entry *fs, volume_t *vol, blkdev_t *dev)
647{
648 /*
649 * Spawn a thread to do the actual checking / mounting in
650 */
651
652 if (vol->worker_running) {
653 LOGE("Worker thread is currently running.. waiting..\n");
654 pthread_mutex_lock(&vol->worker_sem);
655 LOG_VOL("Worker thread now available\n");
656 }
657
658 vol->dev = dev;
659
660 vol->worker_args.fs = fs;
661 vol->worker_args.dev = dev;
662
663 pthread_attr_t attr;
664 pthread_attr_init(&attr);
665 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
666
667 pthread_create(&vol->worker_thread, &attr, volmgr_start_fs_thread, vol);
668
669 return 0;
670}
671
672static void __start_fs_thread_lock_cleanup(void *arg)
673{
674 volume_t *vol = (volume_t *) arg;
675
676 LOG_VOL("__start_fs_thread_lock_cleanup(%s):\n", vol->mount_point);
677
678 vol->worker_running = false;
679
680 // Wake up anyone that was waiting on this thread
681 pthread_mutex_unlock(&vol->worker_sem);
682
683 // Unlock the volume
684 pthread_mutex_unlock(&vol->lock);
685}
686
687static void *volmgr_start_fs_thread(void *arg)
688{
689 volume_t *vol = (volume_t *) arg;
690
691 pthread_cleanup_push(__start_fs_thread_lock_cleanup, arg);
692 pthread_mutex_lock(&vol->lock);
693
694 vol->worker_running = true;
695 vol->worker_pid = getpid();
696
697 struct sigaction actions;
698
699 memset(&actions, 0, sizeof(actions));
700 sigemptyset(&actions.sa_mask);
701 actions.sa_flags = 0;
702 actions.sa_handler = volmgr_start_fs_thread_sighandler;
703 sigaction(SIGUSR1, &actions, NULL);
704
705 struct volmgr_fstable_entry *fs = vol->worker_args.fs;
706 blkdev_t *dev = vol->worker_args.dev;
707 int rc;
708
709 LOG_VOL("Worker thread pid %d starting %s fs %s on %s\n", getpid(), fs->name, dev->dev_fspath, vol->mount_point);
710
711 if (fs->check_fn) {
712 LOG_VOL("Starting %s filesystem check on %s\n", fs->name, dev->dev_fspath);
713 volume_setstate(vol, volstate_checking);
714 pthread_mutex_unlock(&vol->lock);
715 rc = fs->check_fn(dev);
716 pthread_mutex_lock(&vol->lock);
717 if (vol->state != volstate_checking) {
718 LOG_VOL("filesystem check aborted\n");
719 goto out;
720 }
721
722 if (rc < 0) {
723 LOG_VOL("%s filesystem check failed on %s\n", fs->name, dev->dev_fspath);
724 goto out_unmountable;
725 }
726 LOG_VOL("%s filesystem check of %s OK\n", fs->name, dev->dev_fspath);
727 }
728
729 rc = fs->mount_fn(dev, vol);
730 if (!rc) {
731 LOG_VOL("Sucessfully mounted %s filesystem %s on %s\n", fs->name, dev->devpath, vol->mount_point);
732 volume_setstate(vol, volstate_mounted);
733 goto out;
734 }
735
736 LOGE("%s filesystem mount of %s failed (%d)\n", fs->name, dev->devpath, rc);
737
738 out_unmountable:
739 volume_setstate(vol, volstate_damaged);
740 out:
741 pthread_cleanup_pop(1);
742 pthread_exit(NULL);
743 return NULL;
744}
745
746static void volmgr_start_fs_thread_sighandler(int signo)
747{
748 LOGE("volmgr thread got signal %d\n", signo);
749}
750
751static void volume_setstate(volume_t *vol, volume_state_t state)
752{
753 LOG_VOL("Volume %s state change from %d -> %d\n", vol->mount_point, vol->state, state);
754
755 vol->state = state;
756
757 char *prop_val = conv_volstate_to_propstr(vol->state);
758
759 property_set(PROP_EXTERNAL_STORAGE_STATE, prop_val);
760 volume_send_state(vol);
761}
762
763static int volume_send_state(volume_t *vol)
764{
765 char *event = conv_volstate_to_eventstr(vol->state);
766
767 return send_msg_with_data(event, vol->mount_point);
768}
769
770static char *conv_volstate_to_eventstr(volume_state_t state)
771{
772 int i;
773
774 for (i = 0; volume_state_strings[i].event != NULL; i++) {
775 if (volume_state_strings[i].state == state)
776 break;
777 }
778
779 if (!volume_state_strings[i].event)
780 LOGE("conv_volstate_to_eventstr(%d): Invalid state\n", state);
781 return volume_state_strings[i].event;
782}
783
784static char *conv_volstate_to_propstr(volume_state_t state)
785{
786 int i;
787
788 for (i = 0; volume_state_strings[i].event != NULL; i++) {
789 if (volume_state_strings[i].state == state)
790 break;
791 }
792
793 if (!volume_state_strings[i].event)
794 LOGE("conv_volstate_to_propval(%d): Invalid state\n", state);
795 return volume_state_strings[i].property_val;
796}
797