blob: 0aac871d5f57bbb49bef386725c6dbed46f82e4a [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18** mountd automount support
19*/
20
21#include "mountd.h"
22
23#include <pthread.h>
24#include <stdio.h>
25#include <unistd.h>
26#include <string.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <ctype.h>
30#include <pwd.h>
31#include <stdlib.h>
32#include <poll.h>
33
34#include <sys/mount.h>
35#include <sys/stat.h>
36#include <linux/loop.h>
37#include <sys/inotify.h>
38#include <sys/socket.h>
39#include <sys/un.h>
40#include <linux/netlink.h>
41
42#define DEVPATH "/dev/block/"
43#define DEVPATHLENGTH 11 // strlen(DEVPATH)
44
45// FIXME - only one loop mount is supported at a time
46#define LOOP_DEVICE "/dev/block/loop0"
47
48// timeout value for poll() when retries are pending
49#define POLL_TIMEOUT 1000
50
51#define MAX_MOUNT_RETRIES 3
52#define MAX_UNMOUNT_RETRIES 5
53
54typedef enum {
55 // device is unmounted
56 kUnmounted,
57
58 // attempting to mount device
59 kMounting,
60
61 // device is unmounted
62 kMounted,
63
64 // attempting to unmount device
65 // so the media can be removed
66 kUnmountingForEject,
67
68 // attempting to mount device
69 // so it can be shared via USB mass storage
70 kUnmountingForUms,
71} MountState;
72
73typedef struct MountPoint {
74 // block device to mount
75 const char* device;
76
77 // mount point for device
78 const char* mountPoint;
The Android Open Source Project35237d12008-12-17 18:08:08 -080079
80 // path to the UMS driver file for specifying the block device path
81 const char* driverStorePath;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070082
83 // true if device can be shared via
84 // USB mass storage
85 boolean enableUms;
The Android Open Source Project35237d12008-12-17 18:08:08 -080086
87 // Array of ASEC handles
88 void *asecHandles[ASEC_STORES_MAX];
89
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070090 // true if the device is being shared via USB mass storage
91 boolean umsActive;
92
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070093 // current state of the mount point
94 MountState state;
95
96 // number of mount or unmount retries so far,
97 // when attempting to mount or unmount the device
98 int retryCount;
99
100 // next in sMountPointList linked list
101 struct MountPoint* next;
102} MountPoint;
103
104// list of our mount points (does not change after initialization)
105static MountPoint* sMountPointList = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700106boolean gMassStorageEnabled = false;
107boolean gMassStorageConnected = false;
108
109static pthread_t sAutoMountThread = 0;
The Android Open Source Project35237d12008-12-17 18:08:08 -0800110static pid_t gExcludedPids[2] = {-1, -1};
111
112static const char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700113
114// number of mount points that have timeouts pending
115static int sRetriesPending = 0;
116
117// for synchronization between sAutoMountThread and the server thread
118static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
119
120// requests the USB mass_storage driver to begin or end sharing a block device
121// via USB mass storage.
122static void SetBackingStore(MountPoint* mp, boolean enable)
123{
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700124 int fd;
125
The Android Open Source Project35237d12008-12-17 18:08:08 -0800126 if (!mp->driverStorePath) {
127 LOG_ERROR("no driver_store_path specified in config file for %s", mp->device);
128 return;
129 }
130
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700131 LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false"));
The Android Open Source Project35237d12008-12-17 18:08:08 -0800132 fd = open(mp->driverStorePath, O_WRONLY);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700133 if (fd < 0)
134 {
The Android Open Source Project35237d12008-12-17 18:08:08 -0800135 LOG_ERROR("could not open driver_store_path %s\n", mp->driverStorePath);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700136 }
137 else
138 {
139 if (enable)
140 {
141 write(fd, mp->device, strlen(mp->device));
142 mp->umsActive = true;
143 }
144 else
145 {
146 char ch = 0;
147 write(fd, &ch, 1);
148 mp->umsActive = false;
149 }
150 close(fd);
151 }
152}
153
154static boolean ReadMassStorageState()
155{
156 FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
157 if (file)
158 {
159 char buffer[20];
160 fgets(buffer, sizeof(buffer), file);
161 fclose(file);
162 return (strncmp(buffer, "online", strlen("online")) == 0);
163 }
164 else
165 {
166 LOG_ERROR("could not read initial mass storage state\n");
167 return false;
168 }
169}
170
171static boolean IsLoopMounted(const char* path)
172{
173 FILE* f;
174 int count;
175 char device[256];
176 char mount_path[256];
177 char rest[256];
178 int result = 0;
179 int path_length = strlen(path);
180
181 f = fopen("/proc/mounts", "r");
182 if (!f) {
183 LOG_ERROR("could not open /proc/mounts\n");
184 return -1;
185 }
186
187 do {
188 count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
189 if (count == 3) {
190 if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
191 {
192 result = 1;
193 break;
194 }
195 }
196 } while (count == 3);
197
198 fclose(f);
199 LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
200 return result;
201}
202
The Android Open Source Project35237d12008-12-17 18:08:08 -0800203static int CheckFilesystem(const char *device)
204{
205 char cmdline[255];
206 int rc;
207
208 // XXX: SAN: Check for FAT signature
209
210 int result = access(FSCK_MSDOS_PATH, X_OK);
211 if (result != 0) {
212 LOG_MOUNT("CheckFilesystem(%s): fsck_msdos not found (skipping checks)\n", device);
213 return 0;
214 }
215
216 sprintf(cmdline, "%s -p %s", FSCK_MSDOS_PATH, device);
217 LOG_MOUNT("Checking filesystem (%s)\n", cmdline);
218
219 // XXX: Notify framework we're disk checking
220
221 // XXX: PROTECT FROM VIKING KILLER
222 if ((rc = system(cmdline)) < 0) {
223 LOG_ERROR("Error executing disk check command (%d)\n", errno);
224 return -errno;
225 }
226
227 rc = WEXITSTATUS(rc);
228
229 if (rc == 0) {
230 LOG_MOUNT("Filesystem check completed OK\n");
231 return 0;
232 } else if (rc == 1) {
233 LOG_MOUNT("Filesystem check failed (invalid usage)\n");
234 return -EINVAL;
235 } else if (rc == 2) {
236 LOG_MOUNT("Filesystem check failed (unresolved issues)\n");
237 return -EIO;
238 } else if (rc == 4) {
239 LOG_MOUNT("Filesystem check failed (root changed)\n");
240 return -EIO;
241 } else if (rc == 8) {
242 LOG_MOUNT("Filesystem check failed (general failure)\n");
243 return -EIO;
244 } else if (rc == 12) {
245 LOG_MOUNT("Filesystem check failed (exit signaled)\n");
246 return -EIO;
247 } else {
248 LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
249 return -EIO;
250 }
251}
252
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700253static int DoMountDevice(const char* device, const char* mountPoint)
254{
255 LOG_MOUNT("mounting %s at %s\n", device, mountPoint);
256
257#if CREATE_MOUNT_POINTS
258 // make sure mount point exists
259 mkdir(mountPoint, 0000);
260#endif
261
262 int flags = 0;
263
264 if (device && strncmp(device, "/dev/", 5))
265 {
266 // mount with the loop driver if device does not start with "/dev/"
267 int file_fd, device_fd;
268
269 // FIXME - only one loop mount supported at a time
270 file_fd = open(device, O_RDWR);
271 if (file_fd < -1) {
272 LOG_ERROR("open backing file %s failed\n", device);
273 return 1;
274 }
275 device_fd = open(LOOP_DEVICE, O_RDWR);
276 if (device_fd < -1) {
277 LOG_ERROR("open %s failed", LOOP_DEVICE);
278 close(file_fd);
279 return 1;
280 }
281 if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
282 {
283 LOG_ERROR("ioctl LOOP_SET_FD failed\n");
284 close(file_fd);
285 close(device_fd);
286 return 1;
287 }
288
289 close(file_fd);
290 close(device_fd);
291 device = "/dev/block/loop0";
292 }
293
294 int result = access(device, R_OK);
295 if (result != 0)
296 return result;
297
The Android Open Source Project35237d12008-12-17 18:08:08 -0800298 if ((result = CheckFilesystem(device))) {
299 LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
300 // XXX: Notify framework - need a new SDCARD state for the following:
301 // - SD cards which are not present
302 // - SD cards with no partition table
303 // - SD cards with no filesystem
304 // - SD cards with bad filesystem
305 return result;
306 }
307
308
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700309 // Extra safety measures:
310 flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
311 // Also, set fmask = 711 so that files cannot be marked executable,
312 // and cannot by opened by uid 1000 (system). Similar, dmask = 700
313 // so that directories cannot be accessed by uid 1000.
314 result = mount(device, mountPoint, "vfat", flags,
315 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
316 if (result && errno == EROFS) {
317 LOG_ERROR("mount failed EROFS, try again read-only\n");
318 flags |= MS_RDONLY;
319 result = mount(device, mountPoint, "vfat", flags,
320 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
321 }
322 LOG_MOUNT("mount returned %d errno: %d\n", result, errno);
323
324 if (result == 0) {
325 NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800326
327 MountPoint* mp = sMountPointList;
328 while (mp) {
329 if (!strcmp(mountPoint, mp->mountPoint)) {
330 int i;
331
332 for (i = 0; i < ASEC_STORES_MAX; i++) {
333 if (mp->asecHandles[i] != NULL) {
334 int a_result;
335 if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
336 LOG_ERROR("ASEC start failure (%d)\n", a_result);
337 }
338 }
339 }
340 break;
341 }
342 mp = mp -> next;
343 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700344 } else if (errno == EBUSY) {
345 // ignore EBUSY, since it usually means the device is already mounted
346 result = 0;
347 } else {
348#if CREATE_MOUNT_POINTS
349 rmdir(mountPoint);
350#endif
351 LOG_MOUNT("mount failed, errno: %d\n", errno);
352 }
353
354 return result;
355}
356
The Android Open Source Project35237d12008-12-17 18:08:08 -0800357static int DoUnmountDevice(MountPoint *mp)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700358{
The Android Open Source Project35237d12008-12-17 18:08:08 -0800359 boolean loop = IsLoopMounted(mp->mountPoint);
360 int i;
361
362 for (i = 0; i < ASEC_STORES_MAX; i++) {
363 if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i]))
364 AsecStop(mp->asecHandles[i]);
365 }
366
367 int result = umount(mp->mountPoint);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700368 LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
369
370 if (result == 0)
371 {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700372#if CREATE_MOUNT_POINTS
373 rmdir(mountPoint);
374#endif
The Android Open Source Project35237d12008-12-17 18:08:08 -0800375 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
376 }
377
378 if (loop)
379 {
380 // free the loop device
381 int loop_fd = open(LOOP_DEVICE, O_RDONLY);
382 if (loop_fd < -1) {
383 LOG_ERROR("open loop device failed\n");
384 }
385 if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
386 LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
387 }
388
389 close(loop_fd);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700390 }
391
392 // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
393 if (result && (errno == EINVAL || errno == ENOENT))
394 result = 0;
395
396 return result;
397}
398
399static int MountPartition(const char* device, const char* mountPoint)
400{
401 char buf[100];
402 int i;
403
404 // attempt to mount subpartitions of the device
405 for (i = 1; i < 10; i++)
406 {
407 snprintf(buf, sizeof(buf), "%sp%d", device, i);
408 if (DoMountDevice(buf, mountPoint) == 0)
409 return 0;
410 }
411
412 return -1;
413}
414
415/*****************************************************
416 *
417 * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
418 *
419 *****************************************************/
420
421static void SetState(MountPoint* mp, MountState state)
422{
423 mp->state = state;
424}
425
426// Enter a state that requires retries and timeouts.
427static void SetRetries(MountPoint* mp, MountState state)
428{
429 SetState(mp, state);
430 mp->retryCount = 0;
431
432 sRetriesPending++;
433 // wake up the automounter thread if we are being called
434 // from somewhere else with no retries pending
435 if (sRetriesPending == 1 && sAutoMountThread != 0 &&
436 pthread_self() != sAutoMountThread)
437 pthread_kill(sAutoMountThread, SIGUSR1);
438}
439
440// Exit a state that requires retries and timeouts.
441static void ClearRetries(MountPoint* mp, MountState state)
442{
443 SetState(mp, state);
444 sRetriesPending--;
445}
446
447// attempt to mount the specified mount point.
448// set up retry/timeout if it does not succeed at first.
449static void RequestMount(MountPoint* mp)
450{
451 LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
452
453 if (mp->state != kMounted && mp->state != kMounting &&
454 access(mp->device, R_OK) == 0) {
455 // try raw device first
456 if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
457 MountPartition(mp->device, mp->mountPoint) == 0)
458 {
459 SetState(mp, kMounted);
460 }
461 else
462 {
463 SetState(mp, kMounting);
464 mp->retryCount = 0;
465 SetRetries(mp, kMounting);
466 }
467 }
468}
469
470// Force the kernel to drop all caches.
471static void DropSystemCaches(void)
472{
473 int fd;
474
475 LOG_MOUNT("Dropping system caches\n");
476 fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
477
478 if (fd > 0) {
479 char ch = 3;
480 int rc;
481
482 rc = write(fd, &ch, 1);
483 if (rc <= 0)
484 LOG_MOUNT("Error dropping caches (%d)\n", rc);
485 close(fd);
486 }
487}
488
489// attempt to unmount the specified mount point.
490// set up retry/timeout if it does not succeed at first.
491static void RequestUnmount(MountPoint* mp, MountState retryState)
492{
493 int result;
494
495 LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
496
497 if (mp->state == kMounted)
498 {
499 SendUnmountRequest(mp->mountPoint);
500
501 // do this in case the user pulls the SD card before we can successfully unmount
502 sync();
503 DropSystemCaches();
504
The Android Open Source Project35237d12008-12-17 18:08:08 -0800505 if (DoUnmountDevice(mp) == 0)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700506 {
507 SetState(mp, kUnmounted);
508 if (retryState == kUnmountingForUms)
509 {
510 SetBackingStore(mp, true);
511 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
512 }
513 }
514 else
515 {
516 LOG_MOUNT("unmount failed, set retry\n");
517 SetRetries(mp, retryState);
518 }
519 }
520 else if (mp->state == kMounting)
521 {
522 SetState(mp, kUnmounted);
523 }
524}
525
526// returns true if the mount point should be shared via USB mass storage
527static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
528{
529 return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
530}
531
532// handles changes in gMassStorageEnabled and gMassStorageConnected
533static void MassStorageStateChanged()
534{
535 MountPoint* mp = sMountPointList;
536
537 boolean enable = (gMassStorageEnabled && gMassStorageConnected);
538 LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
539
540 while (mp)
541 {
542 if (mp->enableUms)
543 {
544 if (enable)
545 {
546 if (mp->state == kMounting)
547 SetState(mp, kUnmounted);
548 if (mp->state == kUnmounted)
549 {
550 SetBackingStore(mp, true);
551 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
552 }
553 else
554 {
555 LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
556 // need to successfully unmount first
557 RequestUnmount(mp, kUnmountingForUms);
558 }
559 } else if (mp->umsActive) {
560 SetBackingStore(mp, false);
561 if (mp->state == kUnmountingForUms)
562 {
563 ClearRetries(mp, kMounted);
564 NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
565 }
566 else if (mp->state == kUnmounted)
567 {
568 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
569 RequestMount(mp);
570 }
571 }
572 }
573
574 mp = mp->next;
575 }
576}
577
578// called when USB mass storage connected state changes
579static void HandleMassStorageOnline(boolean connected)
580{
581 if (connected != gMassStorageConnected)
582 {
583 gMassStorageConnected = connected;
584 SendMassStorageConnected(connected);
585
586 // we automatically reset to mass storage off after USB is connected
587 if (!connected)
588 gMassStorageEnabled = false;
589
590 MassStorageStateChanged();
591 }
592}
593
594// called when a new block device has been created
595static void HandleMediaInserted(const char* device)
596{
597 MountPoint* mp = sMountPointList;
598
The Android Open Source Project35237d12008-12-17 18:08:08 -0800599 LOG_MOUNT("HandleMediaInserted(%s):\n", device);
600
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700601 while (mp)
602 {
603 // see if the device matches mount point's block device
604 if (mp->state == kUnmounted &&
605 strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
606 {
607 if (MassStorageEnabledForMountPoint(mp))
608 {
609 SetBackingStore(mp, true);
610 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
611 }
612 else
613 RequestMount(mp);
614 }
615 mp = mp->next;
616 }
617}
618
619// called when a new block device has been deleted
620static void HandleMediaRemoved(const char* device)
621{
622 MountPoint* mp = sMountPointList;
623 while (mp)
624 {
625 if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
626 {
627 if (mp->enableUms)
628 SetBackingStore(mp, false);
629
630 if (mp->state == kMounted)
631 {
632 RequestUnmount(mp, kUnmountingForEject);
633 NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
634 }
635
636 NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
637 break;
638 }
639 mp = mp->next;
640 }
641}
642
643// Handle retrying to mount or unmount devices,
644// and handle timeout condition if we have tried too many times
645static void HandleRetries()
646{
647 MountPoint* mp = sMountPointList;
648
649 while (mp)
650 {
651 if (mp->state == kMounting)
652 {
653 if (MountPartition(mp->device, mp->mountPoint) == 0)
654 {
655 // mount succeeded - clear the retry for this mount point
656 ClearRetries(mp, kMounted);
657 }
658 else
659 {
660 mp->retryCount++;
661 if (mp->retryCount == MAX_MOUNT_RETRIES)
662 {
663 // we failed to mount the device too many times
664 ClearRetries(mp, kUnmounted);
665 // notify that we failed to mount
666 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
667 }
668 }
669 }
670 else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
671 {
The Android Open Source Project35237d12008-12-17 18:08:08 -0800672 if (DoUnmountDevice(mp) == 0)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700673 {
674 // unmounting succeeded
675 // start mass storage, if state is kUnmountingForUms
676 if (mp->state == kUnmountingForUms)
677 {
678 SetBackingStore(mp, true);
679 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
680 }
681 // clear the retry for this mount point
682 ClearRetries(mp, kUnmounted);
683 }
684 else
685 {
686 mp->retryCount++;
687 if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
688 {
689 // kill any processes that are preventing the device from unmounting
690 // send SIGKILL instead of SIGTERM if the first attempt did not succeed
691 boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
692
The Android Open Source Project35237d12008-12-17 18:08:08 -0800693 int i;
694
695 for (i = 0; i < ASEC_STORES_MAX; i++) {
696 if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) {
697 LOG_MOUNT("Killing processes for ASEC path '%s'\n",
698 AsecMountPoint(mp->asecHandles[i]));
699 KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]),
700 sigkill,
701 gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t));
702
703 // Now that we've killed the processes, try to stop the volume again
704 AsecStop(mp->asecHandles[i]);
705 }
706 }
707
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700708 // unmounting the device is failing, so start killing processes
The Android Open Source Project35237d12008-12-17 18:08:08 -0800709 KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids,
710 sizeof(gExcludedPids) / sizeof(pid_t));
711
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700712 }
713 }
714 }
715
716 mp = mp->next;
717 }
718}
719
720/*****************************************************
721 *
722 * AUTO-MOUNTER THREAD
723 *
724 *****************************************************/
725
726static void sigusr1_handler(int signo)
727{
728 // don't need to do anything here
729}
730
731// create a socket for listening to inotify events
732int CreateINotifySocket()
733{
734 // initialize inotify
735 int fd = inotify_init();
736
737 if (fd < 0) {
738 LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
739 return -1;
740 }
741
742 fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
743
744 return fd;
745}
746
747
748// create a socket for listening to uevents
749int CreateUEventSocket()
750{
751 struct sockaddr_nl addr;
752 int sz = 64*1024;
753 int fd;
754
755 memset(&addr, 0, sizeof(addr));
756 addr.nl_family = AF_NETLINK;
757 addr.nl_pid = getpid();
758 addr.nl_groups = 0xffffffff;
759
760 fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
761 if(fd < 0)
762 {
763 LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
764 return -1;
765 }
766
767 setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
768
769 if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
770 LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
771 close(fd);
772 return -1;
773 }
774
775 return fd;
776}
777
778/*
779 * Automounter main event thread.
780 * This thread listens for block devices being created and deleted via inotify,
781 * and listens for changes in the USB mass storage connected/disconnected via uevents from the
782 * power supply driver.
783 * This thread also handles retries and timeouts for requests to mount or unmount a device.
784 */
785static void* AutoMountThread(void* arg)
786{
787 int inotify_fd;
788 int uevent_fd;
789 int id;
790 struct sigaction actions;
791
The Android Open Source Project35237d12008-12-17 18:08:08 -0800792 gExcludedPids[1] = getpid();
793
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700794 memset(&actions, 0, sizeof(actions));
795 sigemptyset(&actions.sa_mask);
796 actions.sa_flags = 0;
797 actions.sa_handler = sigusr1_handler;
798 sigaction(SIGUSR1, &actions, NULL);
799
800 // initialize inotify
801 inotify_fd = CreateINotifySocket();
802 // watch for files created and deleted in "/dev"
803 inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
804
805 // initialize uevent watcher
806 uevent_fd = CreateUEventSocket();
807 if (uevent_fd < 0)
808 {
809 LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
810 return NULL;
811 }
812
813 while (1)
814 {
815 struct pollfd fds[2];
816 int timeout, result;
817
818#define INOTIFY_IDX 0
819#define UEVENT_IDX 1
820
821 fds[INOTIFY_IDX].fd = inotify_fd;
822 fds[INOTIFY_IDX].events = POLLIN;
823 fds[INOTIFY_IDX].revents = 0;
824 fds[UEVENT_IDX].fd = uevent_fd;
825 fds[UEVENT_IDX].events = POLLIN;
826 fds[UEVENT_IDX].revents = 0;
827
828 // wait for an event or a timeout to occur.
829 // poll() can also return in response to a SIGUSR1 signal
830 timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
831 result = poll(fds, 2, timeout);
832
833 // lock the mutex while we are handling events
834 pthread_mutex_lock(&sMutex);
835
836 // handle inotify notifications for block device creation and deletion
837 if (fds[INOTIFY_IDX].revents == POLLIN)
838 {
839 struct inotify_event event;
840 char buffer[512];
841 int length = read(inotify_fd, buffer, sizeof(buffer));
842 int offset = 0;
843
844 while (length >= (int)sizeof(struct inotify_event))
845 {
846 struct inotify_event* event = (struct inotify_event *)&buffer[offset];
847
848 if (event->mask == IN_CREATE)
849 {
850 LOG_MOUNT("/dev/block/%s created\n", event->name);
851 HandleMediaInserted(event->name);
852 }
853 else if (event->mask == IN_DELETE)
854 {
855 LOG_MOUNT("/dev/block/%s deleted\n", event->name);
856 HandleMediaRemoved(event->name);
857 }
858
859 int size = sizeof(struct inotify_event) + event->len;
860 length -= size;
861 offset += size;
862 }
863 }
864
865 // handle uevent notifications for USB state changes
866 if (fds[UEVENT_IDX].revents == POLLIN)
867 {
868 char buffer[64*1024];
869 int count;
870
871 count = recv(uevent_fd, buffer, sizeof(buffer), 0);
872 if (count > 0) {
873 char* s = buffer;
874 char* end = s + count;
875 char* type = NULL;
876 char* online = NULL;
877 char* switchName = NULL;
878 char* switchState = NULL;
879
880 while (s < end) {
881 if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
882 type = s + strlen("POWER_SUPPLY_TYPE=");
883 else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
884 online = s + strlen("POWER_SUPPLY_ONLINE=");
885 else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
886 switchName = s + strlen("SWITCH_NAME=");
887 else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
888 switchState = s + strlen("SWITCH_STATE=");
889 s += (strlen(s) + 1);
890 }
891
892 // we use the usb_mass_storage switch state to tell us when USB is online
893 if (switchName && switchState &&
894 !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
895 {
896 LOG_MOUNT("USB online\n");
897 HandleMassStorageOnline(true);
898 }
899
900 // and we use the power supply state to tell us when USB is offline
901 // we can't rely on the switch for offline detection because we get false positives
902 // when USB is reenumerated by the host.
903 if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
904 {
905 LOG_MOUNT("USB offline\n");
906 HandleMassStorageOnline(false);
907 }
908 }
909 }
910
911 // handle retries
912 if (sRetriesPending)
913 HandleRetries();
914
915 // done handling events, so unlock the mutex
916 pthread_mutex_unlock(&sMutex);
917 }
918
919 inotify_rm_watch(inotify_fd, id);
920 close(inotify_fd);
921 close(uevent_fd);
922
923 return NULL;
924}
925
926/*****************************************************
927 *
928 * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
929 *
930 *****************************************************/
931
932// Called to enable or disable USB mass storage support
933void EnableMassStorage(boolean enable)
934{
935 pthread_mutex_lock(&sMutex);
936
937 LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
938 gMassStorageEnabled = enable;
939 MassStorageStateChanged();
940 pthread_mutex_unlock(&sMutex);
941 }
942
943// Called to request that the specified mount point be mounted
944void MountMedia(const char* mountPoint)
945{
946 MountPoint* mp = sMountPointList;
The Android Open Source Project35237d12008-12-17 18:08:08 -0800947
948 LOG_MOUNT("MountMedia(%s)\n", mountPoint);
949
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700950 pthread_mutex_lock(&sMutex);
951 while (mp)
952 {
953 if (strcmp(mp->mountPoint, mountPoint) == 0)
954 {
955 if (mp->state == kUnmountingForEject)
956 {
957 // handle the case where we try to remount before we actually unmounted
958 ClearRetries(mp, kMounted);
959 }
960
961 // don't attempt to mount if mass storage is active
962 if (!MassStorageEnabledForMountPoint(mp))
963 RequestMount(mp);
964 }
965
966 mp = mp->next;
967 }
968 pthread_mutex_unlock(&sMutex);
969 }
970
971// Called to request that the specified mount point be unmounted
972void UnmountMedia(const char* mountPoint)
973{
974 MountPoint* mp = sMountPointList;
975
976 pthread_mutex_lock(&sMutex);
977 while (mp)
978 {
979 if (strcmp(mp->mountPoint, mountPoint) == 0)
980 RequestUnmount(mp, kUnmountingForEject);
981
982 mp = mp->next;
983 }
984 pthread_mutex_unlock(&sMutex);
985}
986
987boolean IsMassStorageEnabled()
988{
989 return gMassStorageEnabled;
990}
991
992boolean IsMassStorageConnected()
993{
994 return gMassStorageConnected;
995}
996
997/***********************************************
998 *
999 * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
1000 *
1001 ***********************************************/
1002
The Android Open Source Project35237d12008-12-17 18:08:08 -08001003void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001004{
1005 MountPoint* newMountPoint;
1006
The Android Open Source Project35237d12008-12-17 18:08:08 -08001007 LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001008 // add a new MountPoint to the head of our linked list
1009 newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
1010 newMountPoint->device = device;
1011 newMountPoint->mountPoint = mountPoint;
The Android Open Source Project35237d12008-12-17 18:08:08 -08001012 newMountPoint->driverStorePath = driverStorePath;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001013 newMountPoint->enableUms = enableUms;
1014 newMountPoint->umsActive = false;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001015 newMountPoint->state = kUnmounted;
1016 newMountPoint->retryCount = 0;
1017
1018 // add to linked list
1019 newMountPoint->next = sMountPointList;
1020 sMountPointList = newMountPoint;
The Android Open Source Project35237d12008-12-17 18:08:08 -08001021 return newMountPoint;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001022}
1023
The Android Open Source Project35237d12008-12-17 18:08:08 -08001024int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size,
1025 const char *mount_point, const char *crypt)
1026{
1027 MountPoint *mp = (MountPoint *) Mp;
1028 int i;
1029
1030 for (i = 0; i < ASEC_STORES_MAX; i++) {
1031 if (!mp->asecHandles[i])
1032 break;
1033 }
1034
1035 if (i == ASEC_STORES_MAX) {
1036 LOG_ERROR("Maximum # of ASEC stores exceeded\n");
1037 return -EINVAL;
1038 }
1039
1040 if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt)))
1041 return -1;
1042
1043 return 0;
1044}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001045static void MountDevices()
1046{
1047 MountPoint* mp = sMountPointList;
1048 while (mp)
1049 {
1050 RequestMount(mp);
1051 mp = mp->next;
1052 }
1053}
1054
1055void StartAutoMounter()
1056{
The Android Open Source Project35237d12008-12-17 18:08:08 -08001057 gExcludedPids[0] = getpid();
1058
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001059 gMassStorageConnected = ReadMassStorageState();
1060 LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
1061
1062 MountDevices();
1063 pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
1064}