blob: 0853b41c0dd73bd32549e4cef1b2f2ad0b568a48 [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
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800112static const char FSCK_MSDOS_PATH[] = "/system/bin/dosfsck";
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) {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800212 LOG_MOUNT("CheckFilesystem(%s): %s not found (skipping checks)\n", FSCK_MSDOS_PATH, device);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800213 return 0;
214 }
215
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800216 char *args[7];
217 args[0] = FSCK_MSDOS_PATH;
218 args[1] = "-v";
219 args[2] = "-V";
220 args[3] = "-w";
221 args[4] = "-p";
222 args[5] = device;
223 args[6] = NULL;
The Android Open Source Project35237d12008-12-17 18:08:08 -0800224
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800225 LOG_MOUNT("Checking filesystem on %s\n", device);
226 rc = logwrap(6, args);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800227
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800228 // XXX: We need to be able to distinguish between a FS with an error
229 // and a block device which does not have a FAT fs at all on it
The Android Open Source Project35237d12008-12-17 18:08:08 -0800230 if (rc == 0) {
231 LOG_MOUNT("Filesystem check completed OK\n");
232 return 0;
233 } else if (rc == 1) {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800234 LOG_MOUNT("Filesystem check failed (general failure)\n");
The Android Open Source Project35237d12008-12-17 18:08:08 -0800235 return -EINVAL;
236 } else if (rc == 2) {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800237 LOG_MOUNT("Filesystem check failed (invalid usage)\n");
The Android Open Source Project35237d12008-12-17 18:08:08 -0800238 return -EIO;
239 } else {
240 LOG_MOUNT("Filesystem check failed (unknown exit code %d)\n", rc);
241 return -EIO;
242 }
243}
244
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700245static int DoMountDevice(const char* device, const char* mountPoint)
246{
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800247 LOG_MOUNT("Attempting mount of %s on %s\n", device, mountPoint);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700248
249#if CREATE_MOUNT_POINTS
250 // make sure mount point exists
251 mkdir(mountPoint, 0000);
252#endif
253
254 int flags = 0;
255
256 if (device && strncmp(device, "/dev/", 5))
257 {
258 // mount with the loop driver if device does not start with "/dev/"
259 int file_fd, device_fd;
260
261 // FIXME - only one loop mount supported at a time
262 file_fd = open(device, O_RDWR);
263 if (file_fd < -1) {
264 LOG_ERROR("open backing file %s failed\n", device);
265 return 1;
266 }
267 device_fd = open(LOOP_DEVICE, O_RDWR);
268 if (device_fd < -1) {
269 LOG_ERROR("open %s failed", LOOP_DEVICE);
270 close(file_fd);
271 return 1;
272 }
273 if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
274 {
275 LOG_ERROR("ioctl LOOP_SET_FD failed\n");
276 close(file_fd);
277 close(device_fd);
278 return 1;
279 }
280
281 close(file_fd);
282 close(device_fd);
283 device = "/dev/block/loop0";
284 }
285
286 int result = access(device, R_OK);
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800287 if (result) {
288 LOG_ERROR("Unable to access '%s' (%d)\n", device, errno);
289 return -errno;
290 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700291
The Android Open Source Project35237d12008-12-17 18:08:08 -0800292 if ((result = CheckFilesystem(device))) {
293 LOG_ERROR("Not mounting filesystem due to check failure (%d)\n", result);
294 // XXX: Notify framework - need a new SDCARD state for the following:
295 // - SD cards which are not present
296 // - SD cards with no partition table
297 // - SD cards with no filesystem
298 // - SD cards with bad filesystem
299 return result;
300 }
The Android Open Source Project35237d12008-12-17 18:08:08 -0800301
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700302 // Extra safety measures:
303 flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
304 // Also, set fmask = 711 so that files cannot be marked executable,
305 // and cannot by opened by uid 1000 (system). Similar, dmask = 700
306 // so that directories cannot be accessed by uid 1000.
307 result = mount(device, mountPoint, "vfat", flags,
308 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
309 if (result && errno == EROFS) {
310 LOG_ERROR("mount failed EROFS, try again read-only\n");
311 flags |= MS_RDONLY;
312 result = mount(device, mountPoint, "vfat", flags,
313 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
314 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700315
316 if (result == 0) {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800317 LOG_MOUNT("Partition %s mounted on %s\n", device, mountPoint);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700318 NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800319
320 MountPoint* mp = sMountPointList;
321 while (mp) {
322 if (!strcmp(mountPoint, mp->mountPoint)) {
323 int i;
324
325 for (i = 0; i < ASEC_STORES_MAX; i++) {
326 if (mp->asecHandles[i] != NULL) {
327 int a_result;
328 if ((a_result = AsecStart(mp->asecHandles[i])) < 0) {
329 LOG_ERROR("ASEC start failure (%d)\n", a_result);
330 }
331 }
332 }
333 break;
334 }
335 mp = mp -> next;
336 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700337 } else if (errno == EBUSY) {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800338 LOG_MOUNT("Mount failed (already mounted)\n");
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700339 result = 0;
340 } else {
341#if CREATE_MOUNT_POINTS
342 rmdir(mountPoint);
343#endif
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800344 LOG_MOUNT("Unable to mount %s on %s\n", device, mountPoint);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700345 }
346
347 return result;
348}
349
The Android Open Source Project35237d12008-12-17 18:08:08 -0800350static int DoUnmountDevice(MountPoint *mp)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700351{
The Android Open Source Project35237d12008-12-17 18:08:08 -0800352 boolean loop = IsLoopMounted(mp->mountPoint);
353 int i;
354
355 for (i = 0; i < ASEC_STORES_MAX; i++) {
356 if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i]))
357 AsecStop(mp->asecHandles[i]);
358 }
359
360 int result = umount(mp->mountPoint);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700361 LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
362
363 if (result == 0)
364 {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700365#if CREATE_MOUNT_POINTS
366 rmdir(mountPoint);
367#endif
The Android Open Source Project35237d12008-12-17 18:08:08 -0800368 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
369 }
370
371 if (loop)
372 {
373 // free the loop device
374 int loop_fd = open(LOOP_DEVICE, O_RDONLY);
375 if (loop_fd < -1) {
376 LOG_ERROR("open loop device failed\n");
377 }
378 if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
379 LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
380 }
381
382 close(loop_fd);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700383 }
384
385 // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
386 if (result && (errno == EINVAL || errno == ENOENT))
387 result = 0;
388
389 return result;
390}
391
392static int MountPartition(const char* device, const char* mountPoint)
393{
394 char buf[100];
395 int i;
396
397 // attempt to mount subpartitions of the device
398 for (i = 1; i < 10; i++)
399 {
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800400 int rc;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700401 snprintf(buf, sizeof(buf), "%sp%d", device, i);
The Android Open Source Project5ae090e2009-01-09 17:51:25 -0800402 rc = DoMountDevice(buf, mountPoint);
403 LOG_MOUNT("DoMountDevice(%s, %s) = %d\n", buf, mountPoint, rc);
404 if (rc == 0)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700405 return 0;
406 }
407
408 return -1;
409}
410
411/*****************************************************
412 *
413 * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
414 *
415 *****************************************************/
416
417static void SetState(MountPoint* mp, MountState state)
418{
419 mp->state = state;
420}
421
422// Enter a state that requires retries and timeouts.
423static void SetRetries(MountPoint* mp, MountState state)
424{
425 SetState(mp, state);
426 mp->retryCount = 0;
427
428 sRetriesPending++;
429 // wake up the automounter thread if we are being called
430 // from somewhere else with no retries pending
431 if (sRetriesPending == 1 && sAutoMountThread != 0 &&
432 pthread_self() != sAutoMountThread)
433 pthread_kill(sAutoMountThread, SIGUSR1);
434}
435
436// Exit a state that requires retries and timeouts.
437static void ClearRetries(MountPoint* mp, MountState state)
438{
439 SetState(mp, state);
440 sRetriesPending--;
441}
442
443// attempt to mount the specified mount point.
444// set up retry/timeout if it does not succeed at first.
445static void RequestMount(MountPoint* mp)
446{
447 LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
448
449 if (mp->state != kMounted && mp->state != kMounting &&
450 access(mp->device, R_OK) == 0) {
451 // try raw device first
452 if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
453 MountPartition(mp->device, mp->mountPoint) == 0)
454 {
455 SetState(mp, kMounted);
456 }
457 else
458 {
459 SetState(mp, kMounting);
460 mp->retryCount = 0;
461 SetRetries(mp, kMounting);
462 }
463 }
464}
465
466// Force the kernel to drop all caches.
467static void DropSystemCaches(void)
468{
469 int fd;
470
471 LOG_MOUNT("Dropping system caches\n");
472 fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
473
474 if (fd > 0) {
475 char ch = 3;
476 int rc;
477
478 rc = write(fd, &ch, 1);
479 if (rc <= 0)
480 LOG_MOUNT("Error dropping caches (%d)\n", rc);
481 close(fd);
482 }
483}
484
485// attempt to unmount the specified mount point.
486// set up retry/timeout if it does not succeed at first.
487static void RequestUnmount(MountPoint* mp, MountState retryState)
488{
489 int result;
490
491 LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
492
493 if (mp->state == kMounted)
494 {
495 SendUnmountRequest(mp->mountPoint);
496
497 // do this in case the user pulls the SD card before we can successfully unmount
498 sync();
499 DropSystemCaches();
500
The Android Open Source Project35237d12008-12-17 18:08:08 -0800501 if (DoUnmountDevice(mp) == 0)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700502 {
503 SetState(mp, kUnmounted);
504 if (retryState == kUnmountingForUms)
505 {
506 SetBackingStore(mp, true);
507 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
508 }
509 }
510 else
511 {
512 LOG_MOUNT("unmount failed, set retry\n");
513 SetRetries(mp, retryState);
514 }
515 }
516 else if (mp->state == kMounting)
517 {
518 SetState(mp, kUnmounted);
519 }
520}
521
522// returns true if the mount point should be shared via USB mass storage
523static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
524{
525 return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
526}
527
528// handles changes in gMassStorageEnabled and gMassStorageConnected
529static void MassStorageStateChanged()
530{
531 MountPoint* mp = sMountPointList;
532
533 boolean enable = (gMassStorageEnabled && gMassStorageConnected);
534 LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
535
536 while (mp)
537 {
538 if (mp->enableUms)
539 {
540 if (enable)
541 {
542 if (mp->state == kMounting)
543 SetState(mp, kUnmounted);
544 if (mp->state == kUnmounted)
545 {
546 SetBackingStore(mp, true);
547 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
548 }
549 else
550 {
551 LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
552 // need to successfully unmount first
553 RequestUnmount(mp, kUnmountingForUms);
554 }
555 } else if (mp->umsActive) {
556 SetBackingStore(mp, false);
557 if (mp->state == kUnmountingForUms)
558 {
559 ClearRetries(mp, kMounted);
560 NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
561 }
562 else if (mp->state == kUnmounted)
563 {
564 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
565 RequestMount(mp);
566 }
567 }
568 }
569
570 mp = mp->next;
571 }
572}
573
574// called when USB mass storage connected state changes
575static void HandleMassStorageOnline(boolean connected)
576{
577 if (connected != gMassStorageConnected)
578 {
579 gMassStorageConnected = connected;
580 SendMassStorageConnected(connected);
581
582 // we automatically reset to mass storage off after USB is connected
583 if (!connected)
584 gMassStorageEnabled = false;
585
586 MassStorageStateChanged();
587 }
588}
589
590// called when a new block device has been created
591static void HandleMediaInserted(const char* device)
592{
593 MountPoint* mp = sMountPointList;
594
The Android Open Source Project35237d12008-12-17 18:08:08 -0800595 LOG_MOUNT("HandleMediaInserted(%s):\n", device);
596
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700597 while (mp)
598 {
599 // see if the device matches mount point's block device
600 if (mp->state == kUnmounted &&
601 strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
602 {
603 if (MassStorageEnabledForMountPoint(mp))
604 {
605 SetBackingStore(mp, true);
606 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
607 }
608 else
609 RequestMount(mp);
610 }
611 mp = mp->next;
612 }
613}
614
615// called when a new block device has been deleted
616static void HandleMediaRemoved(const char* device)
617{
618 MountPoint* mp = sMountPointList;
619 while (mp)
620 {
621 if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
622 {
623 if (mp->enableUms)
624 SetBackingStore(mp, false);
625
626 if (mp->state == kMounted)
627 {
628 RequestUnmount(mp, kUnmountingForEject);
629 NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
630 }
631
632 NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
633 break;
634 }
635 mp = mp->next;
636 }
637}
638
639// Handle retrying to mount or unmount devices,
640// and handle timeout condition if we have tried too many times
641static void HandleRetries()
642{
643 MountPoint* mp = sMountPointList;
644
645 while (mp)
646 {
647 if (mp->state == kMounting)
648 {
649 if (MountPartition(mp->device, mp->mountPoint) == 0)
650 {
651 // mount succeeded - clear the retry for this mount point
652 ClearRetries(mp, kMounted);
653 }
654 else
655 {
656 mp->retryCount++;
657 if (mp->retryCount == MAX_MOUNT_RETRIES)
658 {
659 // we failed to mount the device too many times
660 ClearRetries(mp, kUnmounted);
661 // notify that we failed to mount
662 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
663 }
664 }
665 }
666 else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
667 {
The Android Open Source Project35237d12008-12-17 18:08:08 -0800668 if (DoUnmountDevice(mp) == 0)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700669 {
670 // unmounting succeeded
671 // start mass storage, if state is kUnmountingForUms
672 if (mp->state == kUnmountingForUms)
673 {
674 SetBackingStore(mp, true);
675 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
676 }
677 // clear the retry for this mount point
678 ClearRetries(mp, kUnmounted);
679 }
680 else
681 {
682 mp->retryCount++;
683 if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
684 {
685 // kill any processes that are preventing the device from unmounting
686 // send SIGKILL instead of SIGTERM if the first attempt did not succeed
687 boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
688
The Android Open Source Project35237d12008-12-17 18:08:08 -0800689 int i;
690
691 for (i = 0; i < ASEC_STORES_MAX; i++) {
692 if (mp->asecHandles[i] && AsecIsStarted(mp->asecHandles[i])) {
693 LOG_MOUNT("Killing processes for ASEC path '%s'\n",
694 AsecMountPoint(mp->asecHandles[i]));
695 KillProcessesWithOpenFiles(AsecMountPoint(mp->asecHandles[i]),
696 sigkill,
697 gExcludedPids, sizeof(gExcludedPids) / sizeof(pid_t));
698
699 // Now that we've killed the processes, try to stop the volume again
700 AsecStop(mp->asecHandles[i]);
701 }
702 }
703
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700704 // unmounting the device is failing, so start killing processes
The Android Open Source Project35237d12008-12-17 18:08:08 -0800705 KillProcessesWithOpenFiles(mp->mountPoint, sigkill, gExcludedPids,
706 sizeof(gExcludedPids) / sizeof(pid_t));
707
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700708 }
709 }
710 }
711
712 mp = mp->next;
713 }
714}
715
716/*****************************************************
717 *
718 * AUTO-MOUNTER THREAD
719 *
720 *****************************************************/
721
722static void sigusr1_handler(int signo)
723{
724 // don't need to do anything here
725}
726
727// create a socket for listening to inotify events
728int CreateINotifySocket()
729{
730 // initialize inotify
731 int fd = inotify_init();
732
733 if (fd < 0) {
734 LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
735 return -1;
736 }
737
738 fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
739
740 return fd;
741}
742
743
744// create a socket for listening to uevents
745int CreateUEventSocket()
746{
747 struct sockaddr_nl addr;
748 int sz = 64*1024;
749 int fd;
750
751 memset(&addr, 0, sizeof(addr));
752 addr.nl_family = AF_NETLINK;
753 addr.nl_pid = getpid();
754 addr.nl_groups = 0xffffffff;
755
756 fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
757 if(fd < 0)
758 {
759 LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
760 return -1;
761 }
762
763 setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
764
765 if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
766 LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
767 close(fd);
768 return -1;
769 }
770
771 return fd;
772}
773
774/*
775 * Automounter main event thread.
776 * This thread listens for block devices being created and deleted via inotify,
777 * and listens for changes in the USB mass storage connected/disconnected via uevents from the
778 * power supply driver.
779 * This thread also handles retries and timeouts for requests to mount or unmount a device.
780 */
781static void* AutoMountThread(void* arg)
782{
783 int inotify_fd;
784 int uevent_fd;
785 int id;
786 struct sigaction actions;
787
The Android Open Source Project35237d12008-12-17 18:08:08 -0800788 gExcludedPids[1] = getpid();
789
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700790 memset(&actions, 0, sizeof(actions));
791 sigemptyset(&actions.sa_mask);
792 actions.sa_flags = 0;
793 actions.sa_handler = sigusr1_handler;
794 sigaction(SIGUSR1, &actions, NULL);
795
796 // initialize inotify
797 inotify_fd = CreateINotifySocket();
798 // watch for files created and deleted in "/dev"
799 inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
800
801 // initialize uevent watcher
802 uevent_fd = CreateUEventSocket();
803 if (uevent_fd < 0)
804 {
805 LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
806 return NULL;
807 }
808
809 while (1)
810 {
811 struct pollfd fds[2];
812 int timeout, result;
813
814#define INOTIFY_IDX 0
815#define UEVENT_IDX 1
816
817 fds[INOTIFY_IDX].fd = inotify_fd;
818 fds[INOTIFY_IDX].events = POLLIN;
819 fds[INOTIFY_IDX].revents = 0;
820 fds[UEVENT_IDX].fd = uevent_fd;
821 fds[UEVENT_IDX].events = POLLIN;
822 fds[UEVENT_IDX].revents = 0;
823
824 // wait for an event or a timeout to occur.
825 // poll() can also return in response to a SIGUSR1 signal
826 timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
827 result = poll(fds, 2, timeout);
828
829 // lock the mutex while we are handling events
830 pthread_mutex_lock(&sMutex);
831
832 // handle inotify notifications for block device creation and deletion
833 if (fds[INOTIFY_IDX].revents == POLLIN)
834 {
835 struct inotify_event event;
836 char buffer[512];
837 int length = read(inotify_fd, buffer, sizeof(buffer));
838 int offset = 0;
839
840 while (length >= (int)sizeof(struct inotify_event))
841 {
842 struct inotify_event* event = (struct inotify_event *)&buffer[offset];
843
844 if (event->mask == IN_CREATE)
845 {
846 LOG_MOUNT("/dev/block/%s created\n", event->name);
847 HandleMediaInserted(event->name);
848 }
849 else if (event->mask == IN_DELETE)
850 {
851 LOG_MOUNT("/dev/block/%s deleted\n", event->name);
852 HandleMediaRemoved(event->name);
853 }
854
855 int size = sizeof(struct inotify_event) + event->len;
856 length -= size;
857 offset += size;
858 }
859 }
860
861 // handle uevent notifications for USB state changes
862 if (fds[UEVENT_IDX].revents == POLLIN)
863 {
864 char buffer[64*1024];
865 int count;
866
867 count = recv(uevent_fd, buffer, sizeof(buffer), 0);
868 if (count > 0) {
869 char* s = buffer;
870 char* end = s + count;
871 char* type = NULL;
872 char* online = NULL;
873 char* switchName = NULL;
874 char* switchState = NULL;
875
876 while (s < end) {
877 if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
878 type = s + strlen("POWER_SUPPLY_TYPE=");
879 else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
880 online = s + strlen("POWER_SUPPLY_ONLINE=");
881 else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
882 switchName = s + strlen("SWITCH_NAME=");
883 else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
884 switchState = s + strlen("SWITCH_STATE=");
885 s += (strlen(s) + 1);
886 }
887
888 // we use the usb_mass_storage switch state to tell us when USB is online
889 if (switchName && switchState &&
890 !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
891 {
892 LOG_MOUNT("USB online\n");
893 HandleMassStorageOnline(true);
894 }
895
896 // and we use the power supply state to tell us when USB is offline
897 // we can't rely on the switch for offline detection because we get false positives
898 // when USB is reenumerated by the host.
899 if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
900 {
901 LOG_MOUNT("USB offline\n");
902 HandleMassStorageOnline(false);
903 }
904 }
905 }
906
907 // handle retries
908 if (sRetriesPending)
909 HandleRetries();
910
911 // done handling events, so unlock the mutex
912 pthread_mutex_unlock(&sMutex);
913 }
914
915 inotify_rm_watch(inotify_fd, id);
916 close(inotify_fd);
917 close(uevent_fd);
918
919 return NULL;
920}
921
922/*****************************************************
923 *
924 * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
925 *
926 *****************************************************/
927
928// Called to enable or disable USB mass storage support
929void EnableMassStorage(boolean enable)
930{
931 pthread_mutex_lock(&sMutex);
932
933 LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
934 gMassStorageEnabled = enable;
935 MassStorageStateChanged();
936 pthread_mutex_unlock(&sMutex);
937 }
938
939// Called to request that the specified mount point be mounted
940void MountMedia(const char* mountPoint)
941{
942 MountPoint* mp = sMountPointList;
The Android Open Source Project35237d12008-12-17 18:08:08 -0800943
944 LOG_MOUNT("MountMedia(%s)\n", mountPoint);
945
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700946 pthread_mutex_lock(&sMutex);
947 while (mp)
948 {
949 if (strcmp(mp->mountPoint, mountPoint) == 0)
950 {
951 if (mp->state == kUnmountingForEject)
952 {
953 // handle the case where we try to remount before we actually unmounted
954 ClearRetries(mp, kMounted);
955 }
956
957 // don't attempt to mount if mass storage is active
958 if (!MassStorageEnabledForMountPoint(mp))
959 RequestMount(mp);
960 }
961
962 mp = mp->next;
963 }
964 pthread_mutex_unlock(&sMutex);
965 }
966
967// Called to request that the specified mount point be unmounted
968void UnmountMedia(const char* mountPoint)
969{
970 MountPoint* mp = sMountPointList;
971
972 pthread_mutex_lock(&sMutex);
973 while (mp)
974 {
975 if (strcmp(mp->mountPoint, mountPoint) == 0)
976 RequestUnmount(mp, kUnmountingForEject);
977
978 mp = mp->next;
979 }
980 pthread_mutex_unlock(&sMutex);
981}
982
983boolean IsMassStorageEnabled()
984{
985 return gMassStorageEnabled;
986}
987
988boolean IsMassStorageConnected()
989{
990 return gMassStorageConnected;
991}
992
993/***********************************************
994 *
995 * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
996 *
997 ***********************************************/
998
The Android Open Source Project35237d12008-12-17 18:08:08 -0800999void *AddMountPoint(const char* device, const char* mountPoint, const char * driverStorePath, boolean enableUms)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001000{
1001 MountPoint* newMountPoint;
1002
The Android Open Source Project35237d12008-12-17 18:08:08 -08001003 LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s driverStorePath: %s\n", device, mountPoint, driverStorePath);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001004 // add a new MountPoint to the head of our linked list
1005 newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
1006 newMountPoint->device = device;
1007 newMountPoint->mountPoint = mountPoint;
The Android Open Source Project35237d12008-12-17 18:08:08 -08001008 newMountPoint->driverStorePath = driverStorePath;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001009 newMountPoint->enableUms = enableUms;
1010 newMountPoint->umsActive = false;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001011 newMountPoint->state = kUnmounted;
1012 newMountPoint->retryCount = 0;
1013
1014 // add to linked list
1015 newMountPoint->next = sMountPointList;
1016 sMountPointList = newMountPoint;
The Android Open Source Project35237d12008-12-17 18:08:08 -08001017 return newMountPoint;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001018}
1019
The Android Open Source Project35237d12008-12-17 18:08:08 -08001020int AddAsecToMountPoint(void *Mp, const char *name, const char *backing_file, const char *size,
1021 const char *mount_point, const char *crypt)
1022{
1023 MountPoint *mp = (MountPoint *) Mp;
1024 int i;
1025
1026 for (i = 0; i < ASEC_STORES_MAX; i++) {
1027 if (!mp->asecHandles[i])
1028 break;
1029 }
1030
1031 if (i == ASEC_STORES_MAX) {
1032 LOG_ERROR("Maximum # of ASEC stores exceeded\n");
1033 return -EINVAL;
1034 }
1035
1036 if (!(mp->asecHandles[i] = AsecInit(name, mp->mountPoint, backing_file, size, mount_point, crypt)))
1037 return -1;
1038
1039 return 0;
1040}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001041static void MountDevices()
1042{
1043 MountPoint* mp = sMountPointList;
1044 while (mp)
1045 {
1046 RequestMount(mp);
1047 mp = mp->next;
1048 }
1049}
1050
1051void StartAutoMounter()
1052{
The Android Open Source Project35237d12008-12-17 18:08:08 -08001053 gExcludedPids[0] = getpid();
1054
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001055 gMassStorageConnected = ReadMassStorageState();
1056 LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
1057
1058 MountDevices();
1059 pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
1060}