📄 automount.c
字号:
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//*** mountd automount support*/#include "mountd.h"#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include <ctype.h>#include <pwd.h>#include <stdlib.h>#include <poll.h>#include <sys/mount.h>#include <sys/stat.h>#include <linux/loop.h>#include <sys/inotify.h>#include <sys/socket.h>#include <sys/un.h>#include <linux/netlink.h>#define DEVPATH "/dev/block/"#define DEVPATHLENGTH 11 // strlen(DEVPATH)// FIXME - only one loop mount is supported at a time#define LOOP_DEVICE "/dev/block/loop0"// timeout value for poll() when retries are pending#define POLL_TIMEOUT 1000#define MAX_MOUNT_RETRIES 3#define MAX_UNMOUNT_RETRIES 5typedef enum { // device is unmounted kUnmounted, // attempting to mount device kMounting, // device is unmounted kMounted, // attempting to unmount device // so the media can be removed kUnmountingForEject, // attempting to mount device // so it can be shared via USB mass storage kUnmountingForUms,} MountState;typedef struct MountPoint { // block device to mount const char* device; // mount point for device const char* mountPoint; // true if device can be shared via // USB mass storage boolean enableUms; // true if the device is being shared via USB mass storage boolean umsActive; // logical unit number (for UMS) int lun; // current state of the mount point MountState state; // number of mount or unmount retries so far, // when attempting to mount or unmount the device int retryCount; // next in sMountPointList linked list struct MountPoint* next; } MountPoint;// list of our mount points (does not change after initialization)static MountPoint* sMountPointList = NULL;static int sNextLun = 0;boolean gMassStorageEnabled = false;boolean gMassStorageConnected = false;static pthread_t sAutoMountThread = 0;// number of mount points that have timeouts pendingstatic int sRetriesPending = 0;// for synchronization between sAutoMountThread and the server threadstatic pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER;// requests the USB mass_storage driver to begin or end sharing a block device// via USB mass storage.static void SetBackingStore(MountPoint* mp, boolean enable) { char path[PATH_MAX]; int fd; LOG_MOUNT("SetBackingStore enable: %s\n", (enable ? "true" : "false")); snprintf(path, sizeof(path), "/sys/devices/platform/usb_mass_storage/lun%d/file", mp->lun); fd = open(path, O_WRONLY); if (fd < 0) { LOG_ERROR("could not open %s\n", path); } else { if (enable) { write(fd, mp->device, strlen(mp->device)); mp->umsActive = true; } else { char ch = 0; write(fd, &ch, 1); mp->umsActive = false; } close(fd); }}static boolean ReadMassStorageState(){ FILE* file = fopen("/sys/class/switch/usb_mass_storage/state", "r"); if (file) { char buffer[20]; fgets(buffer, sizeof(buffer), file); fclose(file); return (strncmp(buffer, "online", strlen("online")) == 0); } else { LOG_ERROR("could not read initial mass storage state\n"); return false; }}static boolean IsLoopMounted(const char* path){ FILE* f; int count; char device[256]; char mount_path[256]; char rest[256]; int result = 0; int path_length = strlen(path); f = fopen("/proc/mounts", "r"); if (!f) { LOG_ERROR("could not open /proc/mounts\n"); return -1; } do { count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest); if (count == 3) { if (strcmp(LOOP_DEVICE, device) == 0 && strcmp(path, mount_path) == 0) { result = 1; break; } } } while (count == 3); fclose(f); LOG_MOUNT("IsLoopMounted: %s returning %d\n", path, result); return result;}static int DoMountDevice(const char* device, const char* mountPoint){ LOG_MOUNT("mounting %s at %s\n", device, mountPoint);#if CREATE_MOUNT_POINTS // make sure mount point exists mkdir(mountPoint, 0000);#endif int flags = 0; if (device && strncmp(device, "/dev/", 5)) { // mount with the loop driver if device does not start with "/dev/" int file_fd, device_fd; // FIXME - only one loop mount supported at a time file_fd = open(device, O_RDWR); if (file_fd < -1) { LOG_ERROR("open backing file %s failed\n", device); return 1; } device_fd = open(LOOP_DEVICE, O_RDWR); if (device_fd < -1) { LOG_ERROR("open %s failed", LOOP_DEVICE); close(file_fd); return 1; } if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) { LOG_ERROR("ioctl LOOP_SET_FD failed\n"); close(file_fd); close(device_fd); return 1; } close(file_fd); close(device_fd); device = "/dev/block/loop0"; } int result = access(device, R_OK); if (result != 0) return result; // Extra safety measures: flags |= MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC; // Also, set fmask = 711 so that files cannot be marked executable, // and cannot by opened by uid 1000 (system). Similar, dmask = 700 // so that directories cannot be accessed by uid 1000. result = mount(device, mountPoint, "vfat", flags, "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); if (result && errno == EROFS) { LOG_ERROR("mount failed EROFS, try again read-only\n"); flags |= MS_RDONLY; result = mount(device, mountPoint, "vfat", flags, "utf8,uid=1000,gid=1000,fmask=711,dmask=700"); } LOG_MOUNT("mount returned %d errno: %d\n", result, errno); if (result == 0) { NotifyMediaState(mountPoint, MEDIA_MOUNTED, (flags & MS_RDONLY) != 0); } else if (errno == EBUSY) { // ignore EBUSY, since it usually means the device is already mounted result = 0; } else {#if CREATE_MOUNT_POINTS rmdir(mountPoint);#endif LOG_MOUNT("mount failed, errno: %d\n", errno); } return result;}static int DoUnmountDevice(const char* mountPoint){ boolean loop = IsLoopMounted(mountPoint); int result = umount(mountPoint); LOG_MOUNT("umount returned %d errno: %d\n", result, errno); if (result == 0) { if (loop) { // free the loop device int loop_fd = open(LOOP_DEVICE, O_RDONLY); if (loop_fd < -1) { LOG_ERROR("open loop device failed\n"); } if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) { LOG_ERROR("ioctl LOOP_CLR_FD failed\n"); } close(loop_fd); }#if CREATE_MOUNT_POINTS rmdir(mountPoint);#endif NotifyMediaState(mountPoint, MEDIA_UNMOUNTED, false); } // ignore EINVAL and ENOENT, since it usually means the device is already unmounted if (result && (errno == EINVAL || errno == ENOENT)) result = 0; return result;}static int MountPartition(const char* device, const char* mountPoint){ char buf[100]; int i; // attempt to mount subpartitions of the device for (i = 1; i < 10; i++) { snprintf(buf, sizeof(buf), "%sp%d", device, i); if (DoMountDevice(buf, mountPoint) == 0) return 0; } return -1;}/***************************************************** * * AUTO-MOUNTER STATE ENGINE IMPLEMENTATION * *****************************************************/static void SetState(MountPoint* mp, MountState state){ mp->state = state;}// Enter a state that requires retries and timeouts.static void SetRetries(MountPoint* mp, MountState state){ SetState(mp, state); mp->retryCount = 0; sRetriesPending++; // wake up the automounter thread if we are being called // from somewhere else with no retries pending if (sRetriesPending == 1 && sAutoMountThread != 0 && pthread_self() != sAutoMountThread) pthread_kill(sAutoMountThread, SIGUSR1);}// Exit a state that requires retries and timeouts.static void ClearRetries(MountPoint* mp, MountState state){ SetState(mp, state); sRetriesPending--;}// attempt to mount the specified mount point.// set up retry/timeout if it does not succeed at first.static void RequestMount(MountPoint* mp){ LOG_MOUNT("RequestMount %s\n", mp->mountPoint); if (mp->state != kMounted && mp->state != kMounting && access(mp->device, R_OK) == 0) { // try raw device first if (DoMountDevice(mp->device, mp->mountPoint) == 0 || MountPartition(mp->device, mp->mountPoint) == 0) { SetState(mp, kMounted); } else { SetState(mp, kMounting); mp->retryCount = 0; SetRetries(mp, kMounting); } }}// Force the kernel to drop all caches.static void DropSystemCaches(void){ int fd; LOG_MOUNT("Dropping system caches\n"); fd = open("/proc/sys/vm/drop_caches", O_WRONLY); if (fd > 0) { char ch = 3; int rc; rc = write(fd, &ch, 1); if (rc <= 0) LOG_MOUNT("Error dropping caches (%d)\n", rc); close(fd); }}// attempt to unmount the specified mount point.// set up retry/timeout if it does not succeed at first.static void RequestUnmount(MountPoint* mp, MountState retryState){ int result; LOG_MOUNT("RequestUnmount %s retryState: %d\n", mp->mountPoint, retryState); if (mp->state == kMounted) { SendUnmountRequest(mp->mountPoint); // do this in case the user pulls the SD card before we can successfully unmount sync(); DropSystemCaches(); if (DoUnmountDevice(mp->mountPoint) == 0) { SetState(mp, kUnmounted); if (retryState == kUnmountingForUms) { SetBackingStore(mp, true); NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); } } else { LOG_MOUNT("unmount failed, set retry\n"); SetRetries(mp, retryState); } } else if (mp->state == kMounting) { SetState(mp, kUnmounted); }}// returns true if the mount point should be shared via USB mass storagestatic boolean MassStorageEnabledForMountPoint(const MountPoint* mp){ return (gMassStorageEnabled && gMassStorageConnected && mp->enableUms);}// handles changes in gMassStorageEnabled and gMassStorageConnectedstatic void MassStorageStateChanged(){ MountPoint* mp = sMountPointList; boolean enable = (gMassStorageEnabled && gMassStorageConnected); LOG_MOUNT("MassStorageStateChanged enable: %s\n", (enable ? "true" : "false")); while (mp) { if (mp->enableUms) { if (enable) { if (mp->state == kMounting) SetState(mp, kUnmounted); if (mp->state == kUnmounted) { SetBackingStore(mp, true); NotifyMediaState(mp->mountPoint, MEDIA_SHARED, false); } else { LOG_MOUNT("MassStorageStateChanged requesting unmount\n"); // need to successfully unmount first RequestUnmount(mp, kUnmountingForUms);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -