blob: bfe8ad105cf6e8e0499bb2be8a62025d6e0b787d [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;
79
80 // true if device can be shared via
81 // USB mass storage
82 boolean enableUms;
83
84 // true if the device is being shared via USB mass storage
85 boolean umsActive;
86
87 // logical unit number (for UMS)
88 int lun;
89
90 // current state of the mount point
91 MountState state;
92
93 // number of mount or unmount retries so far,
94 // when attempting to mount or unmount the device
95 int retryCount;
96
97 // next in sMountPointList linked list
98 struct MountPoint* next;
99} MountPoint;
100
101// list of our mount points (does not change after initialization)
102static MountPoint* sMountPointList = NULL;
103static int sNextLun = 0;
104boolean gMassStorageEnabled = false;
105boolean gMassStorageConnected = false;
106
107static pthread_t sAutoMountThread = 0;
108
109// number of mount points that have timeouts pending
110static int sRetriesPending = 0;
111
112// for synchronization between sAutoMountThread and the server thread
113static pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;
114
115// requests the USB mass_storage driver to begin or end sharing a block device
116// via USB mass storage.
117static void SetBackingStore(MountPoint* mp, boolean enable)
118{
119 char path[PATH_MAX];
120 int fd;
121
122 LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false"));
123 snprintf(path, sizeof(path), "/sys/devices/platform/usb_mass_storage/lun%d/file", mp->lun);
124 fd = open(path, O_WRONLY);
125 if (fd < 0)
126 {
127 LOG_ERROR("could not open %s\n", path);
128 }
129 else
130 {
131 if (enable)
132 {
133 write(fd, mp->device, strlen(mp->device));
134 mp->umsActive = true;
135 }
136 else
137 {
138 char ch = 0;
139 write(fd, &ch, 1);
140 mp->umsActive = false;
141 }
142 close(fd);
143 }
144}
145
146static boolean ReadMassStorageState()
147{
148 FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r");
149 if (file)
150 {
151 char buffer[20];
152 fgets(buffer, sizeof(buffer), file);
153 fclose(file);
154 return (strncmp(buffer, "online", strlen("online")) == 0);
155 }
156 else
157 {
158 LOG_ERROR("could not read initial mass storage state\n");
159 return false;
160 }
161}
162
163static boolean IsLoopMounted(const char* path)
164{
165 FILE* f;
166 int count;
167 char device[256];
168 char mount_path[256];
169 char rest[256];
170 int result = 0;
171 int path_length = strlen(path);
172
173 f = fopen("/proc/mounts", "r");
174 if (!f) {
175 LOG_ERROR("could not open /proc/mounts\n");
176 return -1;
177 }
178
179 do {
180 count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
181 if (count == 3) {
182 if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0)
183 {
184 result = 1;
185 break;
186 }
187 }
188 } while (count == 3);
189
190 fclose(f);
191 LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result);
192 return result;
193}
194
195static int DoMountDevice(const char* device, const char* mountPoint)
196{
197 LOG_MOUNT("mounting %s at %s\n", device, mountPoint);
198
199#if CREATE_MOUNT_POINTS
200 // make sure mount point exists
201 mkdir(mountPoint, 0000);
202#endif
203
204 int flags = 0;
205
206 if (device && strncmp(device, "/dev/", 5))
207 {
208 // mount with the loop driver if device does not start with "/dev/"
209 int file_fd, device_fd;
210
211 // FIXME - only one loop mount supported at a time
212 file_fd = open(device, O_RDWR);
213 if (file_fd < -1) {
214 LOG_ERROR("open backing file %s failed\n", device);
215 return 1;
216 }
217 device_fd = open(LOOP_DEVICE, O_RDWR);
218 if (device_fd < -1) {
219 LOG_ERROR("open %s failed", LOOP_DEVICE);
220 close(file_fd);
221 return 1;
222 }
223 if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0)
224 {
225 LOG_ERROR("ioctl LOOP_SET_FD failed\n");
226 close(file_fd);
227 close(device_fd);
228 return 1;
229 }
230
231 close(file_fd);
232 close(device_fd);
233 device = "/dev/block/loop0";
234 }
235
236 int result = access(device, R_OK);
237 if (result != 0)
238 return result;
239
240 // Extra safety measures:
241 flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;
242 // Also, set fmask = 711 so that files cannot be marked executable,
243 // and cannot by opened by uid 1000 (system). Similar, dmask = 700
244 // so that directories cannot be accessed by uid 1000.
245 result = mount(device, mountPoint, "vfat", flags,
246 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
247 if (result && errno == EROFS) {
248 LOG_ERROR("mount failed EROFS, try again read-only\n");
249 flags |= MS_RDONLY;
250 result = mount(device, mountPoint, "vfat", flags,
251 "utf8,uid=1000,gid=1000,fmask=711,dmask=700");
252 }
253 LOG_MOUNT("mount returned %d errno: %d\n", result, errno);
254
255 if (result == 0) {
256 NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0);
257 } else if (errno == EBUSY) {
258 // ignore EBUSY, since it usually means the device is already mounted
259 result = 0;
260 } else {
261#if CREATE_MOUNT_POINTS
262 rmdir(mountPoint);
263#endif
264 LOG_MOUNT("mount failed, errno: %d\n", errno);
265 }
266
267 return result;
268}
269
270static int DoUnmountDevice(const char* mountPoint)
271{
272 boolean loop = IsLoopMounted(mountPoint);
273 int result = umount(mountPoint);
274 LOG_MOUNT("umount returned %d errno: %d\n", result, errno);
275
276 if (result == 0)
277 {
278 if (loop)
279 {
280 // free the loop device
281 int loop_fd = open(LOOP_DEVICE, O_RDONLY);
282 if (loop_fd < -1) {
283 LOG_ERROR("open loop device failed\n");
284 }
285 if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
286 LOG_ERROR("ioctl LOOP_CLR_FD failed\n");
287 }
288
289 close(loop_fd);
290 }
291
292#if CREATE_MOUNT_POINTS
293 rmdir(mountPoint);
294#endif
295 NotifyMediaState(mountPoint, MEDIA_UNMOUNTED, false);
296 }
297
298 // ignore EINVAL and ENOENT, since it usually means the device is already unmounted
299 if (result && (errno == EINVAL || errno == ENOENT))
300 result = 0;
301
302 return result;
303}
304
305static int MountPartition(const char* device, const char* mountPoint)
306{
307 char buf[100];
308 int i;
309
310 // attempt to mount subpartitions of the device
311 for (i = 1; i < 10; i++)
312 {
313 snprintf(buf, sizeof(buf), "%sp%d", device, i);
314 if (DoMountDevice(buf, mountPoint) == 0)
315 return 0;
316 }
317
318 return -1;
319}
320
321/*****************************************************
322 *
323 * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION
324 *
325 *****************************************************/
326
327static void SetState(MountPoint* mp, MountState state)
328{
329 mp->state = state;
330}
331
332// Enter a state that requires retries and timeouts.
333static void SetRetries(MountPoint* mp, MountState state)
334{
335 SetState(mp, state);
336 mp->retryCount = 0;
337
338 sRetriesPending++;
339 // wake up the automounter thread if we are being called
340 // from somewhere else with no retries pending
341 if (sRetriesPending == 1 && sAutoMountThread != 0 &&
342 pthread_self() != sAutoMountThread)
343 pthread_kill(sAutoMountThread, SIGUSR1);
344}
345
346// Exit a state that requires retries and timeouts.
347static void ClearRetries(MountPoint* mp, MountState state)
348{
349 SetState(mp, state);
350 sRetriesPending--;
351}
352
353// attempt to mount the specified mount point.
354// set up retry/timeout if it does not succeed at first.
355static void RequestMount(MountPoint* mp)
356{
357 LOG_MOUNT("RequestMount %s\n", mp->mountPoint);
358
359 if (mp->state != kMounted && mp->state != kMounting &&
360 access(mp->device, R_OK) == 0) {
361 // try raw device first
362 if (DoMountDevice(mp->device, mp->mountPoint) == 0 ||
363 MountPartition(mp->device, mp->mountPoint) == 0)
364 {
365 SetState(mp, kMounted);
366 }
367 else
368 {
369 SetState(mp, kMounting);
370 mp->retryCount = 0;
371 SetRetries(mp, kMounting);
372 }
373 }
374}
375
376// Force the kernel to drop all caches.
377static void DropSystemCaches(void)
378{
379 int fd;
380
381 LOG_MOUNT("Dropping system caches\n");
382 fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
383
384 if (fd > 0) {
385 char ch = 3;
386 int rc;
387
388 rc = write(fd, &ch, 1);
389 if (rc <= 0)
390 LOG_MOUNT("Error dropping caches (%d)\n", rc);
391 close(fd);
392 }
393}
394
395// attempt to unmount the specified mount point.
396// set up retry/timeout if it does not succeed at first.
397static void RequestUnmount(MountPoint* mp, MountState retryState)
398{
399 int result;
400
401 LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState);
402
403 if (mp->state == kMounted)
404 {
405 SendUnmountRequest(mp->mountPoint);
406
407 // do this in case the user pulls the SD card before we can successfully unmount
408 sync();
409 DropSystemCaches();
410
411 if (DoUnmountDevice(mp->mountPoint) == 0)
412 {
413 SetState(mp, kUnmounted);
414 if (retryState == kUnmountingForUms)
415 {
416 SetBackingStore(mp, true);
417 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
418 }
419 }
420 else
421 {
422 LOG_MOUNT("unmount failed, set retry\n");
423 SetRetries(mp, retryState);
424 }
425 }
426 else if (mp->state == kMounting)
427 {
428 SetState(mp, kUnmounted);
429 }
430}
431
432// returns true if the mount point should be shared via USB mass storage
433static boolean MassStorageEnabledForMountPoint(const MountPoint* mp)
434{
435 return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);
436}
437
438// handles changes in gMassStorageEnabled and gMassStorageConnected
439static void MassStorageStateChanged()
440{
441 MountPoint* mp = sMountPointList;
442
443 boolean enable = (gMassStorageEnabled && gMassStorageConnected);
444 LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false"));
445
446 while (mp)
447 {
448 if (mp->enableUms)
449 {
450 if (enable)
451 {
452 if (mp->state == kMounting)
453 SetState(mp, kUnmounted);
454 if (mp->state == kUnmounted)
455 {
456 SetBackingStore(mp, true);
457 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
458 }
459 else
460 {
461 LOG_MOUNT("MassStorageStateChanged requesting unmount\n");
462 // need to successfully unmount first
463 RequestUnmount(mp, kUnmountingForUms);
464 }
465 } else if (mp->umsActive) {
466 SetBackingStore(mp, false);
467 if (mp->state == kUnmountingForUms)
468 {
469 ClearRetries(mp, kMounted);
470 NotifyMediaState(mp->mountPoint, MEDIA_MOUNTED, false);
471 }
472 else if (mp->state == kUnmounted)
473 {
474 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTED, false);
475 RequestMount(mp);
476 }
477 }
478 }
479
480 mp = mp->next;
481 }
482}
483
484// called when USB mass storage connected state changes
485static void HandleMassStorageOnline(boolean connected)
486{
487 if (connected != gMassStorageConnected)
488 {
489 gMassStorageConnected = connected;
490 SendMassStorageConnected(connected);
491
492 // we automatically reset to mass storage off after USB is connected
493 if (!connected)
494 gMassStorageEnabled = false;
495
496 MassStorageStateChanged();
497 }
498}
499
500// called when a new block device has been created
501static void HandleMediaInserted(const char* device)
502{
503 MountPoint* mp = sMountPointList;
504
505 while (mp)
506 {
507 // see if the device matches mount point's block device
508 if (mp->state == kUnmounted &&
509 strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
510 {
511 if (MassStorageEnabledForMountPoint(mp))
512 {
513 SetBackingStore(mp, true);
514 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
515 }
516 else
517 RequestMount(mp);
518 }
519 mp = mp->next;
520 }
521}
522
523// called when a new block device has been deleted
524static void HandleMediaRemoved(const char* device)
525{
526 MountPoint* mp = sMountPointList;
527 while (mp)
528 {
529 if (strncmp(device, mp->device + DEVPATHLENGTH, strlen(mp->device) - DEVPATHLENGTH) == 0)
530 {
531 if (mp->enableUms)
532 SetBackingStore(mp, false);
533
534 if (mp->state == kMounted)
535 {
536 RequestUnmount(mp, kUnmountingForEject);
537 NotifyMediaState(mp->mountPoint, MEDIA_BAD_REMOVAL, false);
538 }
539
540 NotifyMediaState(mp->mountPoint, MEDIA_REMOVED, false);
541 break;
542 }
543 mp = mp->next;
544 }
545}
546
547// Handle retrying to mount or unmount devices,
548// and handle timeout condition if we have tried too many times
549static void HandleRetries()
550{
551 MountPoint* mp = sMountPointList;
552
553 while (mp)
554 {
555 if (mp->state == kMounting)
556 {
557 if (MountPartition(mp->device, mp->mountPoint) == 0)
558 {
559 // mount succeeded - clear the retry for this mount point
560 ClearRetries(mp, kMounted);
561 }
562 else
563 {
564 mp->retryCount++;
565 if (mp->retryCount == MAX_MOUNT_RETRIES)
566 {
567 // we failed to mount the device too many times
568 ClearRetries(mp, kUnmounted);
569 // notify that we failed to mount
570 NotifyMediaState(mp->mountPoint, MEDIA_UNMOUNTABLE, false);
571 }
572 }
573 }
574 else if (mp->state == kUnmountingForEject || mp->state == kUnmountingForUms)
575 {
576 if (DoUnmountDevice(mp->mountPoint) == 0)
577 {
578 // unmounting succeeded
579 // start mass storage, if state is kUnmountingForUms
580 if (mp->state == kUnmountingForUms)
581 {
582 SetBackingStore(mp, true);
583 NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false);
584 }
585 // clear the retry for this mount point
586 ClearRetries(mp, kUnmounted);
587 }
588 else
589 {
590 mp->retryCount++;
591 if (mp->retryCount >= MAX_UNMOUNT_RETRIES)
592 {
593 // kill any processes that are preventing the device from unmounting
594 // send SIGKILL instead of SIGTERM if the first attempt did not succeed
595 boolean sigkill = (mp->retryCount > MAX_UNMOUNT_RETRIES);
596
597 // unmounting the device is failing, so start killing processes
598 KillProcessesWithOpenFiles(mp->mountPoint, sigkill);
599 }
600 }
601 }
602
603 mp = mp->next;
604 }
605}
606
607/*****************************************************
608 *
609 * AUTO-MOUNTER THREAD
610 *
611 *****************************************************/
612
613static void sigusr1_handler(int signo)
614{
615 // don't need to do anything here
616}
617
618// create a socket for listening to inotify events
619int CreateINotifySocket()
620{
621 // initialize inotify
622 int fd = inotify_init();
623
624 if (fd < 0) {
625 LOG_ERROR("inotify_init failed, %s\n", strerror(errno));
626 return -1;
627 }
628
629 fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
630
631 return fd;
632}
633
634
635// create a socket for listening to uevents
636int CreateUEventSocket()
637{
638 struct sockaddr_nl addr;
639 int sz = 64*1024;
640 int fd;
641
642 memset(&addr, 0, sizeof(addr));
643 addr.nl_family = AF_NETLINK;
644 addr.nl_pid = getpid();
645 addr.nl_groups = 0xffffffff;
646
647 fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
648 if(fd < 0)
649 {
650 LOG_ERROR("could not create NETLINK_KOBJECT_UEVENT socket\n");
651 return -1;
652 }
653
654 setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
655
656 if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
657 LOG_ERROR("could not bind NETLINK_KOBJECT_UEVENT socket\n");
658 close(fd);
659 return -1;
660 }
661
662 return fd;
663}
664
665/*
666 * Automounter main event thread.
667 * This thread listens for block devices being created and deleted via inotify,
668 * and listens for changes in the USB mass storage connected/disconnected via uevents from the
669 * power supply driver.
670 * This thread also handles retries and timeouts for requests to mount or unmount a device.
671 */
672static void* AutoMountThread(void* arg)
673{
674 int inotify_fd;
675 int uevent_fd;
676 int id;
677 struct sigaction actions;
678
679 memset(&actions, 0, sizeof(actions));
680 sigemptyset(&actions.sa_mask);
681 actions.sa_flags = 0;
682 actions.sa_handler = sigusr1_handler;
683 sigaction(SIGUSR1, &actions, NULL);
684
685 // initialize inotify
686 inotify_fd = CreateINotifySocket();
687 // watch for files created and deleted in "/dev"
688 inotify_add_watch(inotify_fd, DEVPATH, IN_CREATE|IN_DELETE);
689
690 // initialize uevent watcher
691 uevent_fd = CreateUEventSocket();
692 if (uevent_fd < 0)
693 {
694 LOG_ERROR("CreateUEventSocket failed, %s\n", strerror(errno));
695 return NULL;
696 }
697
698 while (1)
699 {
700 struct pollfd fds[2];
701 int timeout, result;
702
703#define INOTIFY_IDX 0
704#define UEVENT_IDX 1
705
706 fds[INOTIFY_IDX].fd = inotify_fd;
707 fds[INOTIFY_IDX].events = POLLIN;
708 fds[INOTIFY_IDX].revents = 0;
709 fds[UEVENT_IDX].fd = uevent_fd;
710 fds[UEVENT_IDX].events = POLLIN;
711 fds[UEVENT_IDX].revents = 0;
712
713 // wait for an event or a timeout to occur.
714 // poll() can also return in response to a SIGUSR1 signal
715 timeout = (sRetriesPending ? POLL_TIMEOUT : -1);
716 result = poll(fds, 2, timeout);
717
718 // lock the mutex while we are handling events
719 pthread_mutex_lock(&sMutex);
720
721 // handle inotify notifications for block device creation and deletion
722 if (fds[INOTIFY_IDX].revents == POLLIN)
723 {
724 struct inotify_event event;
725 char buffer[512];
726 int length = read(inotify_fd, buffer, sizeof(buffer));
727 int offset = 0;
728
729 while (length >= (int)sizeof(struct inotify_event))
730 {
731 struct inotify_event* event = (struct inotify_event *)&buffer[offset];
732
733 if (event->mask == IN_CREATE)
734 {
735 LOG_MOUNT("/dev/block/%s created\n", event->name);
736 HandleMediaInserted(event->name);
737 }
738 else if (event->mask == IN_DELETE)
739 {
740 LOG_MOUNT("/dev/block/%s deleted\n", event->name);
741 HandleMediaRemoved(event->name);
742 }
743
744 int size = sizeof(struct inotify_event) + event->len;
745 length -= size;
746 offset += size;
747 }
748 }
749
750 // handle uevent notifications for USB state changes
751 if (fds[UEVENT_IDX].revents == POLLIN)
752 {
753 char buffer[64*1024];
754 int count;
755
756 count = recv(uevent_fd, buffer, sizeof(buffer), 0);
757 if (count > 0) {
758 char* s = buffer;
759 char* end = s + count;
760 char* type = NULL;
761 char* online = NULL;
762 char* switchName = NULL;
763 char* switchState = NULL;
764
765 while (s < end) {
766 if (!strncmp("POWER_SUPPLY_TYPE=", s, strlen("POWER_SUPPLY_TYPE=")))
767 type = s + strlen("POWER_SUPPLY_TYPE=");
768 else if (!strncmp("POWER_SUPPLY_ONLINE=", s, strlen("POWER_SUPPLY_ONLINE=")))
769 online = s + strlen("POWER_SUPPLY_ONLINE=");
770 else if (!strncmp("SWITCH_NAME=", s, strlen("SWITCH_NAME=")))
771 switchName = s + strlen("SWITCH_NAME=");
772 else if (!strncmp("SWITCH_STATE=", s, strlen("SWITCH_STATE=")))
773 switchState = s + strlen("SWITCH_STATE=");
774 s += (strlen(s) + 1);
775 }
776
777 // we use the usb_mass_storage switch state to tell us when USB is online
778 if (switchName && switchState &&
779 !strcmp(switchName, "usb_mass_storage") && !strcmp(switchState, "online"))
780 {
781 LOG_MOUNT("USB online\n");
782 HandleMassStorageOnline(true);
783 }
784
785 // and we use the power supply state to tell us when USB is offline
786 // we can't rely on the switch for offline detection because we get false positives
787 // when USB is reenumerated by the host.
788 if (type && online && !strcmp(type, "USB") && !strcmp(online, "0"))
789 {
790 LOG_MOUNT("USB offline\n");
791 HandleMassStorageOnline(false);
792 }
793 }
794 }
795
796 // handle retries
797 if (sRetriesPending)
798 HandleRetries();
799
800 // done handling events, so unlock the mutex
801 pthread_mutex_unlock(&sMutex);
802 }
803
804 inotify_rm_watch(inotify_fd, id);
805 close(inotify_fd);
806 close(uevent_fd);
807
808 return NULL;
809}
810
811/*****************************************************
812 *
813 * THESE FUNCTIONS ARE CALLED FROM THE SERVER THREAD
814 *
815 *****************************************************/
816
817// Called to enable or disable USB mass storage support
818void EnableMassStorage(boolean enable)
819{
820 pthread_mutex_lock(&sMutex);
821
822 LOG_MOUNT("EnableMassStorage %s\n", (enable ? "true" : "false"));
823 gMassStorageEnabled = enable;
824 MassStorageStateChanged();
825 pthread_mutex_unlock(&sMutex);
826 }
827
828// Called to request that the specified mount point be mounted
829void MountMedia(const char* mountPoint)
830{
831 MountPoint* mp = sMountPointList;
832
833 pthread_mutex_lock(&sMutex);
834 while (mp)
835 {
836 if (strcmp(mp->mountPoint, mountPoint) == 0)
837 {
838 if (mp->state == kUnmountingForEject)
839 {
840 // handle the case where we try to remount before we actually unmounted
841 ClearRetries(mp, kMounted);
842 }
843
844 // don't attempt to mount if mass storage is active
845 if (!MassStorageEnabledForMountPoint(mp))
846 RequestMount(mp);
847 }
848
849 mp = mp->next;
850 }
851 pthread_mutex_unlock(&sMutex);
852 }
853
854// Called to request that the specified mount point be unmounted
855void UnmountMedia(const char* mountPoint)
856{
857 MountPoint* mp = sMountPointList;
858
859 pthread_mutex_lock(&sMutex);
860 while (mp)
861 {
862 if (strcmp(mp->mountPoint, mountPoint) == 0)
863 RequestUnmount(mp, kUnmountingForEject);
864
865 mp = mp->next;
866 }
867 pthread_mutex_unlock(&sMutex);
868}
869
870boolean IsMassStorageEnabled()
871{
872 return gMassStorageEnabled;
873}
874
875boolean IsMassStorageConnected()
876{
877 return gMassStorageConnected;
878}
879
880/***********************************************
881 *
882 * THESE FUNCTIONS ARE CALLED ONLY AT STARTUP
883 *
884 ***********************************************/
885
886void AddMountPoint(const char* device, const char* mountPoint, boolean enableUms)
887{
888 MountPoint* newMountPoint;
889
890 LOG_MOUNT("AddMountPoint device: %s, mountPoint: %s\n", device, mountPoint);
891 // add a new MountPoint to the head of our linked list
892 newMountPoint = (MountPoint *)malloc(sizeof(MountPoint));
893 newMountPoint->device = device;
894 newMountPoint->mountPoint = mountPoint;
895 newMountPoint->enableUms = enableUms;
896 newMountPoint->umsActive = false;
897 if (enableUms)
898 newMountPoint->lun = sNextLun++;
899 newMountPoint->state = kUnmounted;
900 newMountPoint->retryCount = 0;
901
902 // add to linked list
903 newMountPoint->next = sMountPointList;
904 sMountPointList = newMountPoint;
905}
906
907static void MountDevices()
908{
909 MountPoint* mp = sMountPointList;
910 while (mp)
911 {
912 RequestMount(mp);
913 mp = mp->next;
914 }
915}
916
917void StartAutoMounter()
918{
919 gMassStorageConnected = ReadMassStorageState();
920 LOG_MOUNT(gMassStorageConnected ? "USB online\n" : "USB offline\n");
921
922 MountDevices();
923 pthread_create(&sAutoMountThread, NULL, AutoMountThread, NULL);
924}